Bad channels / track users / scan users script

By Qc on Aug 08, 2009

Gives detailed info about the people on your channels. Keeps users showing bad channels or bad words anywhere in their /whois results from your channels.
Provides dialogs and custom windows, everything is configurable, is multinetwork, will not make mIRC hang and will not cause flood disconnects.

  • CheckChans is list channels to guard.
  • TrackUsers is a versatile Rule sets-system that are matched towards the whois results, exclude/include/wildcard/regex.
  • BadChans is used to store considered-bad channels
  • BadStrings is for words inside channelnames
  • GoodChans serves as exclude for BadStrings.
  • CheckNicks is a powerful nick scanner with as purpose to monitor suspected nicks and build up a datafile with all the seen nicks/hosts/channels/etc.

//edits 2009/08/08

  • fixed TrackUsers syntax error missing )
  • replaced 'CHANNEL' with the correct 'PCHANNEL' on 2 places (alias cck.TrackUsers.Check)
  • whois output now says which TrackUsers RuleSet was matched.

//version 1.07 (2009/08/10)

  • TrackUsers has now an extra available action (a member of a RuleSet) named 'DO/LIST'. When added to a RuleSet, it will make the script doing /list #channel for every channel the nick is on, so you can see the amount users on the channel and the topic.
    This has an unsolvable issue though, since mIRC creates/activates a 'channels list' window for every /list, it looks like a flashlight. To lower the annoyancy-level abit, I close the window upon every /list raw, you're able to type fairly well in another window then.

//edit 2009/08/16: a manual /names #channel doesn't trigger a channel scan anymore, only after you join.

//version 1.081 (2009/12/12)

  • fixed check channels presence check that failed on entries for any network (*,channel)
  • fixed dialog wrong control id for DO/LIST SetDefault button
  • queue priority choices now centralized in an alias, easier to make sure things do not get out of order
  • added filebased temp/perm system for bans, acts as blacklist to ban immediately upon join.

//version 1.09 (2009/12/26)

  • added CheckNicks system, it's used to monitor certain nicks by /whois'sing them at a high rate.
    This allows to gather alot information about a nick over time (and also multinetwork) and thus helps you to still recognize people that try to make themselves appear different by setting other nicks/identd's/fullnames/take other hosts.
    Note: the 'HUNT' feature is so far only implemented in the CheckNicks data structure, nothing more, so the button has no usage at the moment.

//version 1.10 (2010/01/06)

  • CheckNicks/changed parameter name NETWORK to NICK in data files (more logical)
  • CheckNicks/added parameter STATUS, if OFF then this nick will not be monitored (allows to stop without removing+losing data files)
  • CheckNicks/added parameter value 'LOG' to parameter 'Upon nickchange' script will add the new nick to the datafile of the original nick.
  • CheckNicks/added global Config setting "CheckNicks.StopWhenIRCOP", this will make the script switch parameter STATUS off when the nick is detected as service or operator.
  • CheckNicks/added for CHANNEL entries in datafiles the amount users found on the channel (filled/updated if HUNT is enabled and no common channel)
  • CheckNicks/added HUNT channel selection parameters MinUsers and OnlyRegular.
  • CheckNicks/fixed mode history addition not working.
  • CheckNicks/HUNT feature coded. This will attempt to keep a common channel with the nick.
  • CheckNicks/added parameter GLOBALUPDATES, will perform various data updates on all nicks when they become available.
  • Added /whois raw tags IMMUNETOFILTERING CGI:IRC MODES SOFTCALLERID CALLERID
  • Fixed queue stall in case ":Server load is temporarily too heavy. Please wait a while and try again.". The previous command will be resend.
  • Fixed queue stall in case server doesn't respond /whois (apparently due to whoissed nick in netsplit shortly afterwards) - queue timeout system (which acts as last resort) failed due to a wrong condition
  • Fixed /LIST raw handling bug, happening when more /list commands in the queue, that caused the first channel result being thrown away and thus not updated.
  • Chosed to make the script NOT do /LIST on channels with wildcard (*?) containing names, because /LIST knows wildcards and returns then all matching channels, and to avoid introducing a risk of false matches. These channels will thus only get a usercount in the data if they are joined. The HUNT feature takes this all into account when chosing hunt channels.
  • Improved popup menu and CheckNicks dialog tab.

//version 1.11

  • CheckNicks/fixed bug that could cause multiple occurrences of the same NICK entry in the data
  • CheckNicks/fixed nonsorting after a globaldata update
  • CheckNicks/fixed amount users staying ? for new channels that the user joins but parts before list results arrive. It will now put '0' there to make /list was done
  • CheckNicks/added entry-specific parameter 'AllNicks'. When ON the script will not only whois the CheckNicks list nick, but also every found nick in the data.
  • CheckNicks/added global parameter 'MaxNicksToWhois'. This limits the amount nicks found in the entries' data that it will /whois.

//version 1.12 (2010/01/24)
; - CheckNicks/fixed hunt channel selection selecting also not latest present channels, moved all related code to one function.
; - CheckNicks/changed LIST results now updating usercount always global (all entries) to avoid multiple same LIST requests in case entries have common channels.
; - Queue/command /whois - raws are now haltdef'fed in case another nick matches due to irc protocol treating {}| and []\ as equal
; - CheckNicks/fixed bug in QUIT event that caused a data NICK addition with $null
; - Queue/fixed wrong default priority 1 for menu/dialog issued whois
; - CheckNicks/added channel topic to data file
; - CheckNicks/data windows are now list style, added various popup menu items to copy data to clipboard (to compensate loss due to list style), and to delete lines
; - CheckNicks/data windows popup menu allows to perform a /LIST on wildcard containing channelnames. Do NOT use this if the channelname matches alot channels.
; - CheckNicks/data windows do not have a wintaskbar/switchbar/treebar button anymore and are linked to their connection (in case network not *)
; - Bans/fixed failing dialog list update after removal.
; - CheckNicks/huntjoined-channels will now be parted when the last detection is longer than 2 times the check interval.

//version 1.13 (2012/02/18)
; - CheckNicks/fixed bad condition in AddGlobalDatathat caused channelnames containing wildcards to be accepted and thus potentially update data for a wrong channel.
; - CheckNicks/fixed wrong default priority 1 for menu/dialog issued /list
; - CheckNicks/removed last check time from dialog to avoid the constant updating need
; - CheckNicks/fixed not writing data when globally adding in case no datafile is present.
; - CheckNicks/fixed unwanted condition causing no whois if nothing in CheckChans list.
; - CheckNicks/fixed wrong condition logic causing to not autostart whois for a new added nick.

; ChanCheck by CQ.
; Note to editors: the *.Load aliases allow to set defaults which will be always added upon start.
; This makes it easier to share the script with custom defaults to rookies.
; notes: the SwiftIRC network Unreal3.2.6.SwiftIRC(1) is hardcoded because of just a 401 and no 318 for /whois nonexistantnick

;===================================================================================================
; STARTUP / SHUTDOWN
;===================================================================================================

on *:START: {
  var %dir = $scriptdir
  var %h = cck.HDisk | hmake %h 30
  hadd %h Config $+(%dir,cck.Config.txt)
  hadd %h CheckChans $+(%dir,cck.CheckChans.txt)
  hadd %h CheckNicks $+(%dir,cck.CheckNicks.txt)
  var %p = %dir $+ CheckNicksData\ | hadd %h CheckNicksData %p | if (!$isdir(%p)) { mkdir $qt(%p) }
  hadd %h BadChans $+(%dir,cck.BadChans.txt)
  hadd %h BadStrings $+(%dir,cck.BadStrings.txt)
  hadd %h GoodChans $+(%dir,cck.GoodChans.txt)
  hadd %h TrackUsers $+(%dir,cck.TrackUsers.txt)
  hadd %h Bans $+(%dir,cck.Bans.txt)

  hmake cck.H 30
  cck.Config.Load
  cck.CheckChans.Load
  cck.CheckNicks.Load
  cck.BadChans.Load
  cck.BadStrings.Load
  cck.GoodChans.Load
  cck.TrackUsers.Load

  var %i = 1, %prt = $cck.queue.priorities | while (%i <= %prt) { hmake cck.HQ $+ %i 30 | inc %i }
  hmake cck.HRaw 30
  window -h @cck.raw
  .timercck.TimePolling -oi 0 $hget(cck.HConfig,TimePollingInterval) cck.TimePolling
  cck.CheckChans.Sweep CONTINUE
  cck.CheckChans.Timer
  cck.CheckNicks.Timer
  .disable #cck.*
  cck.queue.process ON SILENT
  echo -bflmrst Loaded $hget(cck.HConfig,VersionShort)
}

on *:UNLOAD: {
  cck.CheckNicks.Save IFCHANGED
  var %s = $hget(cck.HConfig,VersionShort)
  hfree -w cck.H*
  .timercck.* OFF
  close -@ @cck.*
  if ($dialog(CCK.ASK)) { dialog -k CCK.ASK }
  if ($dialog(CCK.MAIN)) { dialog -k CCK.MAIN }
  echo -bflmrst Unloaded %s
}

on *:EXIT: { cck.CheckNicks.Save IFCHANGED }

;===================================================================================================
; GENERAL FUNCTION BASE
;===================================================================================================

; +++ UI OUTPUT FUNCTIONS
alias cck.echo+ {
  var %w = @CCK.Control | if (!$window(%w)) { window -den %w -1 -1 $hget(cck.HConfig,WinXY) }
  echo -t %w (3CCK) $1-
}
alias cck.echo/ {
  var %w = @CCK.Control | if (!$window(%w)) { window -den %w -1 -1 $hget(cck.HConfig,WinXY) }
  echo -t %w (7CCK) $1-
}
alias cck.echo- {
  var %w = @CCK.Control | if (!$window(%w)) { window -den %w -1 -1 $hget(cck.HConfig,WinXY) }
  echo -t %w (4CCK) $1-
}
alias cck.echoLog {
  var %w = @CCK.Log | if (!$window(%w)) { window -den %w -1 -1 $hget(cck.HConfig,WinXY) }
  echo -ti4 %w $1-
}

; +++ FUNCTION / TIMESTAMP IN CONFIGURED FORMAT (!spaces not allowed!)
alias cck.time { 
  if ($1 == $null) { return $asctime($hget(cck.HConfig,TimeFormat)) }
  return $asctime($1,$hget(cck.HConfig,TimeFormat))
}

; +++ FUNCTION / UPDATE TIMER INTERVAL
; IN32 $1 = timer name (wildcards allowed) $2 = new interval
alias cck.timerupd {
  var %label = $1, %new = $2, %total = $timer(0), %i = 1
  while (%i <= %total) {
    var %n = $timer(%i) | if (%label !iswm %n) { inc %i | continue }
    if ($timer(%n).reps == 1) && (%new > $timer(%n).delay) { var %passed = $calc($timer(%n).delay - $timer(%n).secs) | dec %new %passed }
    var %sw = -c
    if ($timer(%n).type == offline) { var %sw = %sw $+ o }
    if ($timer(%n).anysc) { var %sw = %sw $+ i }
    if ($timer(%n).mmt) { var %sw = %sw $+ h }
    if ($timer(%n).pause) { var %sw = %sw $+ p }
    .timer $+ %n %sw $timer(%n).reps %new $timer(%n).com
    inc %i 
  }
}

; +++ FUNCTION / CHECK IF NICK SHOULD BE EXCLUDED FROM CHECKS
; IN32 $1 = network $2 = nick
; $3 = channel that should be skipped in the onchan checks (IAL update after script finishes) or * (no CheckChans presence required)
; OUT32: $result = 1 (exclude) or 0 (no exclude)
alias cck.exclude {
  var %cid = $cck.net2cid($1) | if (!%cid) { return 0 } | if ($cid != %cid) { scid %cid }
  if ($istok($replace($hget(cck.HConfig,ExcludeNicks),<ME>,$me),$2,32)) { return 1 }
  var %s
  if ($hget(cck.HConfig,Exclude@) == ON) { %s = %s $+ @ }
  if ($hget(cck.HConfig,Exclude%) == ON) { %s = %s $+ % }
  if ($hget(cck.HConfig,Exclude+) == ON) { %s = %s $+ + }
  if (%s) && (!$cck.onchan(%s,$1,$2,$3)) { return 1 }
  return 0
}

; +++ FUNCTION KEYS / ACT
; IN $1 = WARN or KICK or BAN
alias cck.key.act {
  cck.diaASK.getlatest $network | if (!$result) { cck.echo- SYSTEM: Unable to act - no data for $network found in ASK dialog. | return }
  var %act = $1 | tokenize 32 $result
  cck.act %act $3 $4 $5 $iif($2 == T,$5-,$cck.actmsg(%act,$3,$4,$6-))
}

; +++ FUNCTION KEYS / RETURN UI MESSAGE
alias cck.key.info {
  var %h = cck.HConfig
  return $+([,$hget(%h,keySweep),) Sweep] $+([,$hget(%h,keyWarn),) Warn] $+([,$hget(%h,keyKick),) Kick] $+([,$hget(%h,keyBan),) Ban]
}

; +++ FUNCTION KEYS / SET THROUGH POPUP MENU
alias cck.key.config {
  var %i = 0
  :nkey
  inc %i | if (%i > 4) { return }
  var %act = $gettok(Sweep@Warn@Kick@Ban,%i,64)
  if ($?="Enter a Function key combination  (ex. SF10)  for: %act " == $null) goto nkey
  cck.Config Key $+ %act $upper($!) | if ($result == REFUSED) { dec %count }
  goto nkey
}

; +++ FUNCTION / PLAY OR SET BAD USER - SOUND AT ASK-MOMENT
; <SET (optional)>
alias cck.BadSound {
  if ($1 != SET) {
    var %f = $hget(cck.HConfig,BadSound) | if ($isfile(%f)) { splay $qt(%f) }
    return 
  }
  cck.Config BadSound $sfile($+($scriptdir,*.wav),Select the sound file to play upon detection/ask or cancel to clear.)
}

; +++ FUNCTION / POPUP DIALOG TO CONFIRM IMPORTANT ACTION
alias cck.confirm { if ($input($1-,yvw) == $yes) { return 1 } | return 0 }

; +++ FUNCTION / CONVERT A WINDOW TOKEN POSITION COLUMN TO A TOKEN STRING
; convert list column to token string
; $1 = @win $2 = ascii token separator input window lines $3 = token position of wanted column
; $4 = ascii token separator output string $5 = max chars output string (optional) $6 = text to append when truncated (optional)
; when $6 = <TOKEN> truncation will happen on token boundary lower and closest to maxchars
alias cck.col2tok { 
  var %i = 1, %t = $line($1,0), %out
  while (%i <= %t) {
    var %out1 = $+(%out,$chr($4),$gettok($line($1,%i),$3,$2))
    if ($5) && ($len(%out1) > $5) { if ($6 == $null) { return %out } | else { return %out $+ $6 } }
    var %out = %out1 | inc %i
  }
  return $mid(%out,2)
}

; +++ FUNCTION / LIMIT A TOKEN STRING TO X CHARS BY DELETING FROM END.
; Input: $1 = separator ascii code $2 = bytes limit $3- = token string
alias cck.limittokenstring { var %s = $3- | while ($len(%s) > $2) { var %s = $deltok(%s,-1,$1) } | return %s }

; +++ FUNCTION / CREATE DATA STRUCTURES WITH RANDOM NAME
alias -l rhash { var %i = 1, %h = cck.Htmp | while ($hget(%h $+ %i)) { inc %i } | hmake %h $+ %i | return %h $+ %i }
alias -l rwin { var %i = 1, %w = @cck.tmp | while ($window(%w $+ %i)) { inc %i } | window -h %w $+ %i | return %w $+ %i }

; +++ FUNCTION / CONVERT SCRIPTS FUNCTION RETURN VALUES TO BOOLEAN
alias -l ok { if ($istok(1 DONE,$1,32)) { return 1 } | return 0 }

; +++ FUNCTION / VARIOUS
alias -l box { return $+([,$1-,],) }
alias -l rs { return $eval($str($ $+ rand(a,z) $ $+ + $ $+ + $chr(32),$1),2) }
alias -l s2c { return $replace($1-,$chr(32),$chr(44)) }

;===================================================================================================
; CONFIG MAIN
;===================================================================================================

; +++ CONFIG / LOAD
alias cck.Config.Load {
  var %h = cck.HConfig | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,Config) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.config.load1 $1

  hadd %h VersionShort ChanCheck 1.12
  hadd %h VersionLong ChanCheck 1.12 by CQ

  %c TimePollingInterval 60
  %c SweepChanInterval 600
  %c SweepNickInterval 30
  %c DeadGap 500
  %c UsePing ON
  %c NoPINGGap 3000
  %c Timeout 15
  %c MonitorQueue ON
  %c NoAbort@PART-KICK OFF
  %c DetailedWhois ON
  %c TimeFormat yyyymmdd-HHnnss
  %c BadSound $+($scriptdir,pervert.wav)

  %c BanDuration 600
  %c ActBadChans ASK
  %c ActBadStrings ASK
  %c WarnMsg Your presence on <BADCHANS> is not allowed in <MYCHANS>. 
  %c KickMsg Illegal channels.
  %c TrackUsers.DefaultAct ASK
  %c TrackUsers.DefaultKickMsg Bad user.
  %c TrackUsers.DefaultDo/List OFF
  %c CheckNicks.ChanFlagHistory 10
  %c CheckNicks.StopWhenIRCOP ON
  %c CheckNicks.MaxNicksToWhois 5

  %c WarnMsg.MaxDataLength 200
  %c KickMsg.MaxDataLength 200
  %c AskMsg.MaxDataLength 200

  %c KeySweep SF12 | alias / $+ SF12 cck.whoischan $!network $!chan CHECKCHANS-UIL
  %c KeyWarn SF9 | alias / $+ SF9 cck.key.act WARN
  %c KeyKick SF10 | alias / $+ SF10 cck.key.act KICK
  %c KeyBan SF11 | alias / $+ SF11 cck.key.act BAN
  %c Exclude@ ON
  %c Exclude% ON
  %c Exclude+ ON
  %c ExcludeNicks X W <ME>

  %c DiaASK.CloseWhenEmpty OFF
  %c DiaASK.MaxDataLength 200
  %c WinLog.ShowNonMatches ON
  %c WinLog.MaxDataLength 800
  %c WinXY 600 300

  hsave %h $qt(%f)
}
alias -l cck.config.load1 { if ($hfind(cck.HConfig,$1,1) == $null) { hadd cck.HConfig $1 $2- } }
alias cck.Config.Default {
  if ($cck.confirm(This is undoable - Sure?)) { .remove $qt($hget(cck.HDisk,Config)) | cck.Config.Load | cck.diaMAIN.load Config }
}

; +++ CONFIG / CHANGE
; IN32 $1 = parameter $2 = value
; OUT32 $result = NOCHANGE or INVALID or DONE
alias cck.Config {
  var %par = $1, %val = $2-
  var %h = cck.HConfig, %val0 = $hget(%h,%par)
  if (%val0 === %val) { cck.echo/ CONFIG:12 %par already " $+ %val $+ ". | return NOCHANGE }

  ; refuse when invalid
  if (Key* iswm %par) {
    if (!$findtok(S F C,$left(%val,1),32)) {
      cck.echo- CONFIG:12 %par Invalid value %val $+ . Valid Function Keys are for ex. SF5 SF6 SF7 SF8
      return INVALID
    }
  }
  elseif ($istok(SweepChanInterval SweepNickInterval DeadGap NoPINGGAP TimePollingInterval BanDuration,%par,32)) {
    if (%val !isnum 1-) || (. isin %val) {
      cck.echo- CONFIG:12 %par Invalid value %val $+ . Should be an integer type-number > 0
      return INVALID
    }
  }
  elseif (%par == TimeFormat) {
    if ($chr(32) isin $asctime(%val)) {
      cck.echo- CONFIG:12 %par Invalid value %val $+ . Spaces are not allowed in the generated time value.
      return INVALID
    }
  }

  hadd %h %par %val | hsave %h $qt($hget(cck.HDisk,Config))

  ; update systems
  goto %par

  :KeySweep | alias / $+ %val cck.whoischan $!network $chr(35) CHECKCHANS-UIL | goto done
  :KeyWarn | alias / $+ %val cck.key.act WARN | goto done
  :KeyKick | alias / $+ %val cck.key.act KICK | goto done
  :KeyBan | alias / $+ %val cck.key.act BAN | goto done
  :SweepChanInterval | cck.timerupd cck.SweepChan %val | goto done
  :SweepNickInterval | cck.timerupd cck.SweepNick %val | goto done
  :DeadGap | :NoPINGGap | if ($timer(cck.DeadGap).delay == %val0) { cck.timerupd cck.DeadGap %val } | goto done
  :Timeout | cck.timerupd cck.Timeout %val | goto done
  :TimePollingInterval | cck.timerupd cck.TimePolling %val | goto done

  :%par
  :done
  cck.diaMAIN.upd Config %par %val
  cck.diaASK.upd %par %val
  cck.echo+ CONFIG:12 %par changed from $qt(%val0) to $qt(%val) $+ . | return DONE
}

; +++ TIMERBASED SYSTEM-POLLING
alias cck.TimePolling {
  cck.Bans.ScanUnban
  cck.CheckNicks.SweepGlobal TIMEPOLLING
}

;===================================================================================================
; CONFIG / CHECK CHANNELS
;===================================================================================================

; +++ CHECK CHANNELS / LOAD
; These are the channels the script should work on and protect.
; <network,channel> = <F.USERJOIN> <F.OWNJOIN> <F.TIMER> <F.CONTINUE>
alias cck.CheckChans.Load {
  var %h = cck.HCheckChans | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,CheckChans) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.checkchans.load1
  ; *****

  ; %c UnderNet,#mp3passion ON OFF OFF OFF
  ; %c UnderNet,#mp3download ON OFF OFF OFF
  ; %c UnderNet,#mp3_collective ON OFF OFF OFF

  ; *****
  hsave %h $qt(%f)
}
alias -l cck.checkchans.load1 { if ($hfind(cck.HCheckChans,$1,1) == $null) { hadd cck.HCheckChans $1 $2- } }

; +++ CHECK CHANNELS / ADD
; IN32 $1 = network or * (= any) $2 = channel $3 = USERJOIN $4 = OWNJOIN $5 = TIMER $6 = CONTINUE
; OUT32 $1 = EXIST or DONE
alias cck.CheckChans.Add {
  var %nc = $s2c($1,$2)
  if ($hfind(cck.HCheckChans,%nc,1)) { cck.echo/ CHECKCHANS:12 %nc Already exists. | return EXIST }
  hadd cck.HCheckChans %nc $3 $4 $5 $6 | hsave cck.HCheckChans $qt($hget(cck.HDisk,CheckChans))
  cck.CheckChans.Timer | cck.echo+ CHECKCHANS:12 %nc Added. $&
    $box(USERJOIN $3) $box(OWNJOIN $4) $box(TIMER $5) $box(CONTINUE $6)
  cck.diaMAIN.upd CheckChans ADD %nc | return DONE
}

; +++ CHECK CHANNELS / REMOVE
; IN32 $1 = network or * $2 = channel
; OUT32 $1 = NOTFOUND or DONE
alias cck.CheckChans.Rem {
  var %nc = $s2c($1,$2)
  if (!$hfind(cck.HCheckChans,%nc,1)) { cck.echo- CHECKCHANS:12 %nc Not found. | return NOTFOUND }
  hdel cck.HCheckChans %nc | hsave cck.HCheckChans $qt($hget(cck.HDisk,CheckChans))
  cck.CheckChans.Timer | cck.echo+ CHECKCHANS: 12 %nc Removed.
  cck.diaMAIN.upd CheckChans REM %nc | return DONE
}

; +++ CHECK CHANNELS / CHANGE PARAMETER
; IN32 $1 = network $2 = channel $3 = parameter (USERJOIN or OWNJOIN or TIMER or CONTINUE) $4 = ON or OFF or command spy
; OUT32 $1 = NOTFOUND or NOCHANGE or DONE
alias cck.CheckChans.Change {
  var %nc = $s2c($1,$2), %rec = $hget(cck.HCheckChans,%nc), %p
  if (%rec == $null) { cck.echo- CHECKCHANS:12 %nc Not found. | return NOTFOUND }
  goto $3
  :USERJOIN | %p = 1 | goto check
  :OWNJOIN | %p = 2 | goto check
  :TIMER | %p = 3 | goto check
  :CONTINUE | %p = 4 | goto check
  :check
  var %val0 = $gettok(%rec,%p,32)
  if ($4 === %val0) { cck.echo/ CHECKCHANS:12 %nc $+  $3 already $4 $+ . | return NOCHANGE }
  hadd cck.HCheckChans %nc $puttok(%rec,$4,%p,32) | hsave cck.HCheckChans $qt($hget(cck.HDisk,CheckChans))
  if ($3 == TIMER) { cck.CheckChans.Timer }
  elseif ($3 == CONTINUE) { cck.CheckChans.Sweep CONTINUE }
  cck.echo+ CHECKCHANS:12 %nc $+  $3 now $4 $+ .
  cck.diaMAIN.upd CheckChans REP %nc | return DONE
}

; +++ CHECK CHANNELS / CHECK IF GIVEN CHANNEL IS IN THE LIST OR CHECK PARAMETER
; IN32 $1 = network $2 = channel $3 = optional parameter (USERJOIN or OWNJOIN or TIMER or CONTINUE or ANY)
; OUT32 case no parameter: 1 = channel present 0 = not present
; OUT32 case parameter: 1 = parameter ON 0 = OFF
alias cck.CheckChans.Check {
  var %hi = $hfind(cck.HCheckChans,$s2c($1,$2),1,W) | if (%hi == $null) { return 0 } | if (!$3) { return 1 }
  var %rec = $hget(cck.HCheckChans,%hi), %event = $3 | tokenize 32 %rec
  goto %event

  :USERJOIN | if ($1 == ON) { return 1 } | return 0
  :OWNJOIN | if ($2 == ON) { return 1 } | return 0
  :TIMER | if ($3 == ON) { return 1 } | return 0
  :CONTINUE | if ($4 == ON) { return 1 } | return 0
  :ANY | if ($1 == ON) || ($2 == ON) || ($3 == ON) || ($4 == ON) { return 1 } | return 0
}

; +++ CHECK CHANNELS / SET DEFAULT
alias cck.CheckChans.Default {
  if ($cck.confirm(This is undoable - Sure?)) {
    .remove $qt($hget(cck.HDisk,CheckChans)) | cck.CheckChans.Load | cck.diaMAIN.load CheckChans
  }
}

; +++ CHECK CHANNELS / GET PARAMETER
; IN32 $1 = network  $2 = channel  $3 = parameter
; OUT32 $1 = NOTFOUND or value of parameter
alias cck.CheckChans.Get {
  var %nc = $s2c($1,$2), %rec = $hget(cck.HCheckChans,%nc) | if (%rec == $null) { return NOTFOUND }
  var %par = $3 | tokenize 32 %rec | goto %par
  :USERJOIN | return $1
  :OWNJOIN | return $2
  :TIMER | return $3
  :CONTINUE | return $4
}

; +++ CHECK CHANNELS / START-STOP TIMER ACCORDING TO CONFIGURATION
alias cck.CheckChans.Timer {
  var %i = 1
  while ($hget(cck.HCheckChans,%i).data) {
    if ($gettok($v1,3,32) == ON) {
      if (!$timer(cck.SweepChan)) { .timercck.SweepChan -oi 0 $hget(cck.HConfig,SweepChanInterval) cck.CheckChans.Sweep TIMER }
      return
    }
    inc %i
  }
  .timercck.SweepChan OFF
}

; +++ CHECK CHANNELS / PERFORM GLOBAL SWEEP
; IN $1 = CONTINUE or TIMER or UIL (optional - if not specified, all channels) 
alias cck.CheckChans.Sweep {
  if ($1 == CONTINUE) && (!$cck.queue.empty(WHOIS,-)) { return }
  var %p1 = $1, %i = 1
  while ($hget(cck.HCheckChans,%i).item) {
    var %nc = $v1 | tokenize 32 $hget(cck.HCheckChans,%nc)
    if (%p1 == TIMER) { if ($3 != ON) { inc %i | continue } }
    elseif (%p1 == CONTINUE) { if ($4 != ON) { inc %i | continue } }
    tokenize 44 %nc
    if ($1 != *) {    
      if ($cck.net2cid($1)) { scid $v1 | if ($me ison $2) { cck.whoischan $1 $2 $+(CHECKCHANS-,%p1) } }
    }
    else { cck.whoischanglobal $2 $+(CHECKCHANS-,%p1) }
    inc %i
  }
}

;===================================================================================================
; CONFIG / CHECK NICKS
;===================================================================================================
; These are the nicks to start monitoring upon connect.
; List hash:
; <network,nick> =
; $1 = interval sec or DEFAULT $2 = ctime last check|Never $3 = HUNT or NOHUNT $4 = ID
; $5 = (nickchange)LOG|ADD|UPDATE|IGNORE> $6 = addition system $7(status) = ON or OFF
; $8 = HuntChanMinUsers $9(HuntChanOnlyRegular) = ON or OFF $10(GlobalUpdates) = ON or OFF $11(AllNicks) = ON or OFF

; /LIST command sysdata layout: <system1-systemX>;<nick>;<user@host>;<CheckNicks-entrynick>

; per user ID data file structure:
; <network> NICK <time last detected> <nick>
; <network> USER <time last detected> <user>
; <network> HOST <time last detected> <host>
; <network> REGNICK <time last detected> <regnick>
; <network> AUTHNAME <time last detected> <authname>
; <network> REALNAME <time last detected> <realname>
; <network> REALHOST <time last detected> <realhost>
; <network> USERIP <time last detected> <ip>
; <network> CHANNEL <time user last detected> <channelname> <?|amount users-ctime last requested> <seen flags(.separated)> <lastXflags(.separated)> <topic>
; <network> MODES <time last detected> <modes>
; <network> AWAY <time last detected> <awaymessage>
; <network> ^IDLE <time last detected> <time>
; <network> ^LOGON <time last detected> <time>
; <network> SSL|HELPOP|FILTERING|IMMUNETOFILTERING|BOT|SERVICE|IRCOP|CGI:IRC|CALLERID|SOFTCALLERID <time last detected> 1

; +++ CHECK NICKS / LOAD
alias cck.CheckNicks.Load {
  var %h = cck.HCheckNicks | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,CheckNicks) | if ($isfile(%f)) { hload %h $qt(%f) }
}

; +++ CHECK NICKS / FIND NEXT FREE ID NUMBER
alias cck.CheckNicks.NextID { var %i = 1 | while ($hfind(cck.HCheckNicks,& & & %i *,1,w).data) { inc %i } | return %i }

; +++ CHECK NICKS / ADD
; IN32 $1 = network or * (= any net, parameter literal-no wildcard treatment) $2 = nick $3 = interval or DEFAULT $4 = HUNT or NOHUNT $5 = UPDATE or ADD or IGNORE
; $6 = system  $7 = ON or OFF $8(Hunt)Minimal users $9(Hunt)OnlyRegular = ON|OFF $10(GlobalUpdates) = ON|OFF $11(AllNicks) = ON|OFF
; OUT32 $1 = EXIST or DONE
alias cck.CheckNicks.Add {
  if ($hget(cck.HCheckNicks,$s2c($1,$2))) { cck.echo/ CHECKNICKS:12 $s2c($1,$2) Already exists. | return EXIST }
  if ($hget(cck.HCheckNicks,$s2c(*,$2))) { cck.echo/ CHECKNICKS:12 $s2c(*,$2) Already exists. | return EXIST }
  var %nn = $s2c($1,$2), %id = $cck.CheckNicks.NextID | hadd cck.HCheckNicks %nn $3 Never $4 %id $5-11 | cck.CheckNicks.Save
  .remove $cck.CheckNicks.ID2file(%id)
  cck.CheckNicks.Timer | cck.echo+ CHECKNICKS:12 %nn (ID %id $+ ) Added by $6 $+ . $&
    $box(Status: $7) $box(Interval $3) $box(NickChange: $5) $box(GlobalUpdates: $10) $box(AllNicks: $11) $box($4) $box(MinUsers: $8) $box(OnlyRegular: $9)
  cck.diaMAIN.upd CheckNicks ADD %nn | return DONE
}

; +++ CHECK NICKS / REMOVE
; IN32 $1 = network or * $2 = nick
; OUT32 $1 = NOTFOUND or DONE
alias cck.CheckNicks.Rem {
  var %nn = $s2c($1,$2), %rec = $hget(cck.HCheckNicks,%nn)
  if (!%rec) { cck.echo- CHECKNICKS:12 %nn Not found. | return NOTFOUND }
  hdel cck.HCheckNicks %nn | cck.CheckNicks.Save
  var %id = $gettok(%rec,4,32), %f = $cck.CheckNicks.ID2file(%id) | if ($isfile(%f)) { .remove $qt(%f) }
  var %w = $+(@CCK.,%nn) | if ($window(%w)) { window -c %w }
  cck.CheckNicks.Timer | cck.echo+ CHECKNICKS:12 %nn (ID %id $+ ) Removed.
  cck.diaMAIN.upd CheckNicks REM %nn | return DONE
}

; +++ CHECK NICKS / WIPE DATA
; IN32 $1 = network or * $2 = nick
; OUT32 $1 = NOTFOUND or DONE
alias cck.CheckNicks.WipeData {
  var %nn = $s2c($1,$2), %rec = $hget(cck.HCheckNicks,%nn)
  if (!%rec) { cck.echo- CHECKNICKS:12 %nn Not found. | return NOTFOUND }
  var %id = $gettok(%rec,4,32), %f = $cck.CheckNicks.ID2file(%id) | if ($isfile(%f)) { .remove $qt(%f) }
  var %w = $+(@CCK.,%nn) | if ($window(%w)) { clear %w }
  cck.echo+ CHECKNICKS:12 %nn (ID %id $+ ) Data wiped.
  return DONE
}

; +++ CHECK NICKS / GET FILE FOR GIVEN ID
alias cck.CheckNicks.ID2file { return $+($hget(cck.HDisk,CheckNicksData),ID,$1,.txt) }

; +++ CHECK NICKS / CHANGE PARAMETER
; IN32 $1 = network or * $2 = nick $3 = parameter $4 = value
; OUT32 $1 = NOTFOUND or NOCHANGE or DONE
alias cck.CheckNicks.Change {
  var %nn = $s2c($1,$2), %rec = $hget(cck.HCheckNicks,%nn), %p
  if (%rec == $null) {
    var %nn = $s2c(*,$2), %rec = $hget(cck.HCheckNicks,%nn)
    if (%rec == $null) {
      if ($3 != CTIME) { cck.echo- CHECKNICKS:12 %nn Not found. }
      return NOTFOUND
    }
  }
  var %p = $findtok(INTERVAL CTIME HUNT ID NICKCHANGE SYSTEM STATUS MINUSERS ONLYREGULAR GLOBALUPDATES ALLNICKS,$3,1,32)
  var %val0 = $gettok(%rec,%p,32)
  if ($4 === %val0) { 
    if ($3 != CTIME) { cck.echo/ CHECKNICKS:12 %nn (ID $gettok(%rec,4,32) $+ ) $3 already $4 $+ . }
    return NOCHANGE
  }
  hadd cck.HCheckNicks %nn $puttok(%rec,$4,%p,32)
  if ($3 == INTERVAL) { cck.CheckNicks.Timer }
  if ($3 != CTIME) { cck.CheckNicks.Save | cck.echo+ CHECKNICKS:12 %nn (ID $gettok(%rec,4,32) $+ ) $3 now $4 $+ . | cck.diaMAIN.upd CheckNicks REP %nn }
  else { hadd cck.H CheckNicks.NotSaved 1 }
  return DONE
}

; +++ CHECK NICKS / UPDATE NICK
; IN32 $1 = network $2 = nick $3 = new nick
alias cck.CheckNicks.UpdateNick {
  var %nn0 = $s2c($1,$2), %h = cck.HCheckNicks, %rec0 = $hget(%h,%nn0)
  if (!%rec0) { tokenize 32 * $2-3 | var %nn0 = $s2c($1,$2), %rec0 = $hget(%h,%nn0) | if (!%rec0) { return } }
  var %action = $gettok(%rec0,5,32) | if (%action == IGNORE) { return }
  var %nn1 = $s2c($1,$3), %rec1 = $hget(%h,%nn1) | if (%rec1) { return }
  if (%action == ADD) {
    hadd %h %nn1 $puttok($puttok($puttok(%rec0,$ctime,2,32),$cck.CheckNicks.NextID,4,32),SYS,6,32)
    cck.diaMAIN.upd CheckNicks ADD %nn1
  }
  elseif (%action == UPDATE) {
    hadd %h %nn1 $puttok(%rec0,$ctime,2,32) | hdel %h %nn0
    var %w = $+(@CCK.,%nn0) | if ($window(%w)) { renwin %w $+(@CCK.,%nn1) }
    cck.diaMAIN.upd CheckNicks REM %nn0
    cck.diaMAIN.upd CheckNicks ADD %nn1
  }
  elseif (%action == LOG) { cck.CheckNicks.AddData $1 $2 - NICK $3 }
  cck.CheckNicks.Save
}

; +++ CHECK NICKS / SAVE HASH
; IN: $1 = IFCHANGED (optional)
alias cck.CheckNicks.Save {
  if ($1 != IFCHANGED) { hsave cck.HCheckNicks $qt($hget(cck.HDisk,CheckNicks)) | hdel cck.H CheckNicks.NotSaved | return }
  if (!$hget(cck.H,CheckNicks.NotSaved)) { return }
  hsave cck.HCheckNicks $qt($hget(cck.HDisk,CheckNicks))
  hdel cck.H CheckNicks.NotSaved
}

; +++ CHECK NICKS / GET PARAMETER
; IN32 $1 = network  $2 = nick  $3 = parameter
; OUT32 $1 = NOTFOUND or value of parameter
alias cck.CheckNicks.Get {
  var %nn = $s2c($1,$2), %rec = $hget(cck.HCheckNicks,%nn)
  if (!%rec) { tokenize 32 * $2-3 | var %rec = $hget(%h,$s2c($1,$2)) | if (!%rec) { return NOTFOUND } }
  return $gettok(%rec,$findtok(INTERVAL CTIME HUNT ID NICKCHANGE SYSTEM STATUS MINUSERS ONLYREGULAR GLOBALUPDATES ALLNICKS,$3,1,32),32)  
}

; +++ CHECK NICKS / START-STOP DEFAULT TIMER ACCORDING TO CONFIGURATION
alias cck.CheckNicks.Timer {
  var %i = 1
  while ($hget(cck.HCheckNicks,%i).data) {
    tokenize 32 $v1
    if ($7 == ON) && ($1 == default) {
      if (!$timer(cck.SweepNick)) { .timercck.SweepNick -oi 0 $hget(cck.HConfig,SweepNickInterval) cck.CheckNicks.SweepGlobal TIMERDEFAULT }
      return
    }
    inc %i
  }
  .timercck.SweepNicks OFF
}

; +++ CHECK NICKS / PERFORM GLOBAL SWEEP
; IN $1 = TIMERDEFAULT or TIMEPOLLING or UIL (optional - if not specified, all channels) 
alias cck.CheckNicks.SweepGlobal {
  var %p1 = $1, %i = 1
  while ($hget(cck.HCheckNicks,%i).item) {
    var %nn = $v1 | tokenize 32 $hget(cck.HCheckNicks,%nn) | if ($7 != ON) { inc %i | continue }
    if (%p1 == TIMERDEFAULT) { if ($1 != DEFAULT) { inc %i | continue } }
    elseif (%p1 == TIMEPOLLING) {
      if ($1 == DEFAULT) { inc %i | continue }
      if ($2 != Never) && ($calc($ctime - $2) < $1) { inc %i | continue }
    }
    tokenize 44 %nn | cck.CheckNicks.SweepNicks $1-2 $+(CHECKNICKS-,%p1,;,$2,;?;,$2)
    if ($3 == ON) { if ($1 == *) { cck.CheckNicks.HuntStartGlobal $1-2 } | else { cck.CheckNicks.HuntStart $1-2 $1 } }
    inc %i
  }
}

; CHECK NICKS / SWEEP FOUND DATA-NICKS FOR GIVEN CN-NICK
; IN $1 = cn-net $2 = cn-nick $3 = sysdata (format system;nick;address;cn-nick)
alias cck.CheckNicks.SweepNicks {
  if ($cck.CheckNicks.Get($1,$2,ALLNICKS) == ON) { var %list = $iif($cck.CheckNicks.GetFoundNicks($1,$2),$v1,$2) } | else { var %list = $2 }
  var %i = 1, %n
  while ($gettok(%list,%i,32)) {
    %n = $v1
    if ($1 == *) { cck.whoisnickglobal %n $puttok($3,%n,2,59) } | else { cck.whoisnick $1 %n $puttok($3,%n,2,59)  }
    inc %i
  }
}

; +++ CHECK NICKS / PERFORM /LIST ON ALL DETECTED CHANNELS
; IN $1 = cn-net $2 = cn-nick $3 = SYS or UIL $4 = ALL or MISSING
; will exclude wildcards *? containing channels
alias cck.CheckNicks.ListChans {
  var %rec = $hget(cck.HCheckNicks,$s2c($1,$2)) | if (!%rec) { return }
  var %net = $1, %nick = $2, %sys = $3, %what = $4 | tokenize 32 %rec | var %id = $4, %f = $cck.CheckNicks.ID2file($4) | if (!$isfile(%f)) { return }
  var %wr = $rwin | filter -fw $qt(%f) %wr & CHANNEL * | if (!$filtered) { window -c %wr | return }
  var %i = 1
  while ($line(%wr,%i)) {
    tokenize 32 $v1 | if (? isin $4) || (* isin $4) { inc %i | continue }
    if (%what == ALL) || ($5 == ?) { cck.queue.add $1 LIST $4 - - $+(CHECKNICKS,-,%sys,;,%nick,;?;,%nick) }
    inc %i
  }
  window -c %wr
}

; +++ CHECK NICKS / GET FOUND NICKS
; IN $1 = cn-net $2 = cn-nick
alias cck.CheckNicks.GetFoundNicks {
  var %rec = $hget(cck.HCheckNicks,$s2c($1,$2)) | if (!%rec) { return 0 }
  tokenize 32 %rec | var %f = $cck.CheckNicks.ID2file($4) | if (!$isfile(%f)) { return 0 }
  var %wr = $rwin | filter -fwteu 3 32 $qt(%f) %wr & NICK * | if (!$filtered) { window -c %wr | return 0 }
  var %list = $gettok($cck.col2tok(%wr,32,4,32,300),$+(1,-,$hget(cck.HConfig,CheckNicks.MaxNicksToWhois)),32)
  window -c %wr | return %list
}

; +++ CHECK NICKS / START HUNT GLOBAL
; IN $1 = cn-net $2 = cn-nick
alias cck.CheckNicks.HuntStartGlobal {
  var %i = 1 | while ($scon(%i)) { if ($scon(%i).status == connected) { cck.CheckNicks.HuntStart $1-2 $scon(%i).network } | inc %i }
}
; +++ CHECK NICKS / HUNT START
; IN $1 = cn-net $2 = cn-nick $3 = net $4 = channel to skip (optional)
alias cck.CheckNicks.HuntStart {
  var %rec = $hget(cck.HCheckNicks,$s2c($1,$2)) | if (!%rec) || ($gettok(%rec,3,32) != HUNT) { return }
  var %f = $cck.CheckNicks.ID2file($gettok(%rec,4,32)) | if (!$isfile(%f)) { return }
  var %wr1 = $rwin, %wr2 = $rwin | loadbuf %wr1 $qt(%f) | filter -ww %wr1 %wr2 $3 NICK * | if (!$filtered) { goto end }
  scid $cck.net2cid($3) | var %i = 1 | while ($line(%wr2,%i)) { if ($comchan($gettok($v1,4,32),1)) { goto end } | inc %i }
  filter -wwcteu 3 32 %wr1 %wr2 $3 CHANNEL * | if (!$filtered) { goto end }
  var %cn-nick = $2, %net = $3, %skip = $4 | tokenize 32 %rec | var %minusers = $8, %onlyreg = $9
  var %i = 1 | while ($line(%wr2,%i)) { tokenize 32 $v1 | if ($5 == ?) && (* !isin $4) && (? !isin $4) { goto end } | inc %i }
  var %i = 1, %c = 0
  while ($line(%wr2,%i)) {
    tokenize 32 $v1 | if ($4 == %skip) || ($5 == ?) { dline %wr2 %i | continue }
    var %users = $gettok($5,1,45) | if (%users < %minusers) { dline %wr2 %i | continue }
    if (%onlyreg == ON) && (($chr(37) isin $6) || (@ isin $6) || (! isin $6) || (& isin $6) || (~ isin $6)) { dline %wr2 %i | continue }
    rline %wr2 %i $3 $4 %users | inc %i
  }
  filter -wwcteu 1 32 %wr2 %wr2 * | tokenize 32 $line(%wr2,1) | filter -wwc %wr2 %wr2 $1 & &
  if ($filtered) { var %c = $gettok($line(%wr2,$rand(1,$v1)),2,32) | cck.queue.add %net JOIN %c - - $+(CHECKNICKS-HUNT,;,%cn-nick) }
  :end | window -c %wr1 | window -c %wr2
}

; +++ CHECKNICKS / HUNT CHANNEL SWITCH
; IN32 $1 = net $2 = new channel or <none> $3 = cn-nick
alias cck.CheckNicks.HuntSwitchChan {
  var %net = $1, %chan = $2, %cn-nick = $3, %hi = $hfind(cck.H,$s2c(CheckNicks-Hunt $1 * $3),1,w)
  if (%hi) {
    hdel cck.H %hi | tokenize 44 %hi | var %chan0 = $3
    if (!$hfind(cck.H,$s2c(CheckNicks-Hunt %net %chan0 *),1,w)) { cck.queue.add %net PART %chan0 - - $+(CHECKNICKS-HUNT,;,-) }
  }
  if (%chan != <none>) { hadd cck.H $s2c(CheckNicks-Hunt %net %chan %cn-nick) 1 }
}

; +++ CHECKNICKS / PART HUNT CHANNEL IF LAST DETECTED IS TOO LONG AGO
; IN32 $1 = net $2 = nick $3 = sysdata (format system;nick;address;cn-nick)
alias cck.CheckNicks.HuntOffline {
  var %net = $1, %nick = $2, %cn-nick = $gettok($3,4,59), %rec = $hget(cck.HCheckNicks,$s2c($1,%cn-nick))
  if (%rec == $null) { var %rec = $hget(cck.HCheckNicks,$s2c(*,%cn-nick)) | if (!%rec) { return } | var %cn-net = * } | else { var %cn-net = %net }
  tokenize 32 %rec | var %f = $cck.CheckNicks.ID2file($4) | if (!$isfile(%f)) { return }
  var %wr = $rwin | filter -fwteu 3 32 $qt(%f) %wr & NICK * | if (!$filtered) { window -c %wr | return }
  var %intval = $iif($1 != default,$1,$hget(cck.HConfig,SweepNickInterval)), %ctime = $gettok($line(%wr,1),3,32)
  if ($calc($ctime - %ctime) > $calc(2 * %intval)) { cck.CheckNicks.HuntSwitchChan %net <none> %cn-nick }
  window -c %wr
}

; +++ CHECK NICKS / DATA - ADD GLOBAL (to all nicks)
; IN32 $1 = net $2 = CHANNEL|HOST $3 = value of $2 $4 = USERS|TOPIC|NICK $5- = value of $4
alias cck.CheckNicks.AddDataGlobal {
  if ($2 == CHANNEL) && ($4 == USERS) { if (* isin $3) || (? isin $3) { return } }
  var %net = $1, %p1 = $2, %v1 = $3, %p2 = $4, %v2 = $5-, %cn-net, %cn-nick, %i = 1, %t = $ctime
  while ($hget(cck.HCheckNicks,%i).item) {
    tokenize 44 $v1 | if ($1 != *) && ($1 != %net) { inc %i | continue }
    %cn-net = $1 | %cn-nick = $2 | tokenize 32 $hget(cck.HCheckNicks,%i).data | if ($10 == OFF) { inc %i | continue }
    var %f = $cck.CheckNicks.ID2file($4) | goto $+(%p1,.,%p2)

    :CHANNEL.USERS
    var %rec = $read(%f,nw,%net CHANNEL & %v1 *) | if (!%rec) { inc %i | continue }
    write -l $+ $readn $qt(%f) $puttok(%rec,$+(%v2,-,%t),5,32) | goto sort
    :CHANNEL.TOPIC
    var %rec = $read(%f,nw,%net CHANNEL & %v1 *) | if (!%rec) || ($gettok(%rec,8-,32) === %v2) { inc %i | continue }
    write -l $+ $readn $qt(%f) $gettok(%rec,1-7,32) %v2 | goto sort
    :HOST.NICK
    if (!$read(%f,nw,%net HOST & %v1)) || ($read(%f,nw,%net NICK & %v2)) { inc %i | continue }
    write $qt(%f) %net NICK %t %v2 | goto sort

    :sort
    filter -ffct 1 32 $qt(%f) $qt(%f) *
    var %w = $+(@CCK.,%cn-net,$chr(44),%cn-nick)
    if ($window(%w)) { cck.CheckNicks.ShowData %cn-net %cn-nick SYS }
    cck.CheckNicks.HuntStart %cn-net %cn-nick %net
    inc %i
  }
}

; +++ CHECK NICKS / DATA -ADD (current connection)
; IN32 $1 = network $2 = nick $3 = sysdata 
; case $4 = NICK $5 = newnick
; case $4 = hash name matching cck.H*, layout parameter=value $5 = @window name, layout <prefixedchannel>
alias cck.CheckNicks.AddData {
  var %net = $1, %nick = $2, %sysdata = $3, %cn-nick = $gettok($3,4,59), %rec = $hget(cck.HCheckNicks,$s2c($1,%cn-nick))
  if (%rec == $null) { var %rec = $hget(cck.HCheckNicks,$s2c(*,%cn-nick)) | if (!%rec) { return NOTFOUND } | var %cn-net = * } | else { var %cn-net = %net }
  if (cck.H* iswm $4) { var %hin = $4, %win = $5, %clrhash } | else { var %hin = $rhash, %clrhash = 1 | hadd %hin $4 $5- }
  tokenize 32 %rec
  var %hunt = $3, %f = $cck.CheckNicks.ID2file($4), %wr = $rwin, %t = $ctime | if ($isfile(%f)) { loadbuf %wr $qt(%f) }

  if ($fline(%wr,%net NICK & %nick,1)) { rline %wr $v1 $puttok($line(%wr,$v1),%t,3,32) } | else { aline %wr %net NICK %t %nick }

  tokenize 32 NICK USER HOST REGNICK AUTHNAME REALNAME REALHOST USERIP MODES AWAY SSL HELPOP FILTERING IMMUNETOFILTERING BOT SERVICE IRCOP CGI:IRC CALLERID SOFTCALLERID
  var %i = 1
  while ($ [ $+ [ %i ] ] != $null) {
    var %p = $v1, %v = $hget(%hin,%p) | if (%v == $null) { inc %i | continue }
    if ($fline(%wr,%net %p & %v,1)) { rline %wr $v1 $puttok($line(%wr,$v1),%t,3,32) } | else { aline %wr %net %p %t %v }
    if ($istok(SERVICE IRCOP,%p,32)) && ($hget(cck.HConfig,CheckNicks.StopWhenIRCOP) == ON) { cck.CheckNicks.Change %cn-net %cn-nick STATUS OFF }
    inc %i
  }

  var %v = $hget(%hin,^LOGON)
  if (%v != $null) {
    var %s = $asctime(%v,$hget(cck.HConfig,TimeFormat))
    if ($fline(%wr,%net ^LOGON & *,1)) { tokenize 32 $line(%wr,$v1) | rline %wr $v1 $1 $2 %t %s }
    else { aline %wr %net ^LOGON %t %s }
  }

  var %v = $hget(%hin,^IDLE)
  if (%v != $null) {
    var %s = $duration($v1)
    if ($fline(%wr,%net ^IDLE & *,1)) { tokenize 32 $line(%wr,$v1) | rline %wr $v1 $1 $2 %t %s }
    else { aline %wr %net ^IDLE %t %s }
  }

  var %i = 0, %dolist
  :channels.next
  inc %i | var %pchan = $line(%win,%i) | if (%pchan == $null) { goto save }
  var %pchanc = $cck.getprefixchan(%net,%pchan)
  if (%pchanc == INVALID) { cck.echo- SYSTEM: Parsing failure ( $+ %net $+ ) for channel12 %pchan $+ . | goto channels.next }
  tokenize 32 %pchanc | var %p = $1, %c = $2
  if ($fline(%wr,%net CHANNEL & %c *,1)) {
    tokenize 32 $line(%wr,$v1)
    rline %wr $v1 %net CHANNEL %t %c $5 $iif(!$istok($6,%p,46),$+(%p,.,$6),$6) $iif($gettok($7,1,46) == %p,$7,$gettok($+(%p,.,$7),$+(1,-,$hget(cck.HConfig,CheckNicks.ChanFlagHistory)),46))) $8-
    if ($5 == ?) && (%hunt == HUNT) { %dolist = 1 }
  }
  else { aline %wr %net CHANNEL %t %c ? %p %p ? | if (%hunt == HUNT) { %dolist = 1 } }
  if ($istok(%sysdata,LIST,59)) || (%dolist) {
    if (? !isin %c) && (* !isin %c) { cck.queue.add %net LIST %c - - $+(CHECKNICKS,;,%nick,;,$hget(%hin,USER),@,$hget(%hin,HOST),;,%cn-nick) }
  }
  goto channels.next
  :save
  filter -wfct 1 32 %wr $qt(%f) * | window -c %wr | if (%clrhash) { hfree %hin }
  var %w = $+(@CCK.,%cn-net,$chr(44),%cn-nick)
  if ($window(%w)) { cck.CheckNicks.ShowData %cn-net %cn-nick SYS | titlebar %w $cck.time(%t) $+ : $+(%nick,!,$hget(%hin,USER),@,$hget(%hin,HOST)) }
  cck.CheckNicks.HuntStart %cn-net %cn-nick %net
}

; IN32 $1 = data window name $2 = line number (assumed list&data presence)
; +++ CHECK NICKS / DATA - DELETE LINE
alias cck.CheckNicks.DeleteDataLine {
  var %w = $1, %l = $2 | tokenize 32 $hget(cck.HCheckNicks,$gettok($1,2-,46)) | write -dl %l $qt($cck.CheckNicks.ID2file($4)) | dline -l %w %l
}

; +++ CHECK NICKS / DATA - SHOW
; IN32 $1 = cn-net $2 = cn-nick $3 = LUI or SYS
alias cck.CheckNicks.ShowData {
  var %net = $1, %nn = $s2c($1,$2), %sys = $3, %rec = $hget(cck.HCheckNicks,%nn) | if (!%rec) { return }
  tokenize 32 %rec | var %f = $cck.CheckNicks.ID2file($4) | if (!$isfile(%f)) { return }
  var %w = @CCK. $+ %nn, %wr
  if (!$window(%w)) {
    if (%net != *) { var %sw = -dw0l, %cid = $cck.net2cid(%net) | if (%cid) { scid %cid } } | else { var %sw = -dw0li }
    window $iif(%sys == LUI,%sw $+ a,%sw) -t8,14,22,40,48,65 +L %w -1 -1 $hget(cck.HConfig,WinXY) | font %w 10 Tahoma
  }
  else { %wr = $rwin | window -h %wr | var %i = 1 | while ($sline(%w,%i)) { aline %wr $v1 | inc %i } }
  loadbuf -r %w $qt(%f) | var %i = 1, %9 = $chr(9), %t = $hget(cck.HConfig,TimeFormat)
  while ($line(%w,%i)) {
    tokenize 32 $v1
    if ($2 != CHANNEL) { rline -l %w %i $+($1,%9,$2,%9,$asctime($3,%t),%9,$4-,%9,-,%9,-) }
    else { rline -l %w %i $+($1,%9,$2,%9,$asctime($3,%t),%9,$4,%9,$iif($5 != ?,$asctime($gettok($5,2,45),%t),$5),%9,$gettok($5,1,45) $6-) }
    inc %i
  }
  var %i = 1, %9 = $chr(9) | while ($line(%wr,%i)) { tokenize 9 $v1 | if ($fline(%w,$+($1,%9,$2,%9,*,%9,$4,%9,*),1,1)) { sline -a %w $v1 } | inc %i }
  window -b %w | if (%wr) { window -c %wr }
  if (%sys == LUI) { window -a %w }
}

;===================================================================================================
; CONFIG / BAD CHANNELS
;===================================================================================================

; +++ BAD CHANNELS / LOAD
alias cck.BadChans.Load {
  var %h = cck.HBadChans | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,BadChans) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.badchans.load1
  ; *****

  ; %c *,#nazi
  ; %c *,#100%highschoolsexpics
  ; %c *,#11-19flirts
  ; %c *,#familysex
  ; %c *,#horsesex
  ; %c *,#femsubmissionsex
  ; %c *,#baby&toddlerlove
  ; %c *,#0!FuckMywife
  ; %c *,#jack-off-str8
  ; %c *,#masturbation
  ; %c *,#phonesex
  ; %c *,#100%pk.highschoolsexpics
  ; %c *,#femhumiliationsex
  ; %c *,#cybersex
  ; %c *,#analsex
  ; %c *,#smalltitpics

  ; *****
  hsave %h $qt(%f)
}
alias -l cck.badchans.load1 { if ($hfind(cck.HBadChans,$1,1,W) == $null) { hadd cck.HBadChans $1 0 Never } }

; +++ BAD CHANNELS / ADD
; IN32 $1 = network or * $2 = channel
; OUT32 $1 = EXIST or DONE
alias cck.BadChans.Add {
  var %nc = $s2c($1,$2)
  if ($hfind(cck.HBadChans,%nc,1,W)) { cck.echo/ BADCHANS:12 %nc Already exists (wildcard match). | return EXIST }
  hadd cck.HBadChans %nc 0 Never | hsave cck.HBadChans $qt($hget(cck.HDisk,BadChans))
  cck.diaMAIN.upd BadChans ADD %nc
  cck.echo+ BADCHANS:12 %nc Added. | return DONE
}

; +++ BAD CHANNELS / REMOVE
; IN32 $1 = network or * $2 = channel
; OUT32 $1 = NOTFOUND or DONE
alias cck.BadChans.Rem {
  var %nc = $s2c($1,$2)
  if (!$hfind(cck.HBadChans,%nc,1)) { cck.echo- BADCHANS:12 %nc Not Found. | return NOTFOUND }
  hdel cck.HBadChans %nc | hsave cck.HBadChans $qt($hget(cck.HDisk,BadChans))
  cck.diaMAIN.upd BadChans REM %nc
  cck.echo+ BADCHANS:12 %nc Removed. | return DONE
}

; +++ BAD CHANNELS / CHECK IF GIVEN CHANNEL IS A MATCH
; IN32 $1 = network $2 = channel
; OUT32 $1 = 1 (match) or 0 (no match)
alias cck.BadChans.Check {
  var %nc = $s2c($1,$2) | if ($hfind(cck.HBadChans,%nc,1,W) == $null) { return 0 }
  var %nc = $v1 | tokenize 32 $hget(cck.HBadChans,%nc) | hadd cck.HBadChans %nc $calc($1 + 1) $ctime
  hsave cck.HBadChans $qt($hget(cck.HDisk,BadChans))
  cck.diaMAIN.upd BadChans REP %nc  
  return 1
}

; +++ BAD CHANNELS / SET DEFAULT
alias cck.BadChans.Default {
  if ($cck.confirm(This is undoable - Sure?)) {
    .remove $qt($hget(cck.HDisk,BadChans)) | cck.BadChans.Load | cck.diaMAIN.load BadChans
  }
}

;===================================================================================================
; CONFIG / BAD STRINGS
;===================================================================================================

; +++ BAD STRINGS / LOAD
alias cck.BadStrings.Load {
  var %h = cck.HBadStrings | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,BadStrings) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.badstrings.load1
  ; *****

  ; %c kid
  ; %c young
  ; %c teen
  ; %c boy
  ; %c girl
  ; %c daughter
  ; %c dad
  ; %c mom
  ; %c incest
  ; %c horse
  ; %c dog
  ; %c animal

  ; *****
  hsave %h $qt(%f)
}
alias -l cck.badstrings.load1 { if ($hfind(cck.HBadStrings,$1,1) == $null) { hadd cck.HBadStrings $1 0 Never } }

; +++ BAD STRINGS / ADD
; IN32 $1 = string
; OUT32 $1 = EXIST or DONE
alias cck.BadStrings.Add {
  if ($hfind(cck.HBadStrings,$1,1)) { cck.echo/ BADSTRINGS:12 $1 Already exists. | return EXIST }
  hadd cck.HBadStrings $1 0 Never | hsave cck.HBadStrings $qt($hget(cck.HDisk,BadStrings))
  cck.diaMAIN.upd BadStrings ADD $1
  cck.echo+ BADSTRINGS:12 $1 Added. | return DONE
}

; +++ BAD STRINGS / REMOVE
; IN32 $1 = string
; OUT32 $1 = NOTFOUND or DONE
alias cck.BadStrings.Rem {
  if (!$hfind(cck.HBadStrings,$1,1)) { cck.echo- BADSTRINGS:12 $1 Not found. | return NOTFOUND }
  hdel cck.HBadStrings $1 | hsave cck.HBadStrings $qt($hget(cck.HDisk,BadStrings))
  cck.diaMAIN.upd BadStrings REM $1
  cck.echo+ BADSTRINGS:12 $1 Removed. | return DONE
}

; +++ BAD STRINGS / CHECK IF GIVEN STRING IS MATCH
; IN32 $1 = string
; OUT32 $1 = 1 (match) or 0 (no match)
alias cck.BadStrings.Check {
  var %c = $1, %h = cck.HBadStrings, %i = 1
  while ($hget(%h,%i).item) {
    var %s = $v1
    if (%s isin %c) {
      tokenize 32 $hget(%h,%s)
      hadd %h %s $calc($1 + 1) $ctime | hsave %h $qt($hget(cck.HDisk,BadStrings))
      cck.diaMAIN.upd BadStrings REP %s
      return 1
    }
    inc %i
  }
  return 0
}

; +++ BAD STRINGS / SET DEFAULTS
alias cck.BadStrings.Default {
  if ($cck.confirm(This is undoable - Sure?)) {
    .remove $qt($hget(cck.HDisk,BadStrings)) | cck.BadStrings.Load | cck.diaMAIN.load BadStrings
  }
}

;===================================================================================================
; CONFIG / GOOD CHANNELS
;===================================================================================================

; +++ GOOD CHANNELS / LOAD
alias cck.GoodChans.Load {
  var %h = cck.HGoodChans | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,GoodChans) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.goodchans.load1
  ; *****

  %c UnderNet,#horsepower
  %c UnderNet,#dogtraining
  %c UnderNet,#paulyoung
  %c UnderNet,#daddy'splace

  ; *****
  hsave %h $qt(%f)
}
alias -l cck.goodchans.load1 { if ($hfind(cck.HGoodChans,$1,1,W) == $null) { hadd cck.HGoodChans $1 0 Never } }

; +++ GOOD CHANNELS / ADD
; IN32 $1 = network or * $2 = channel
; OUT32: $1 = EXIST or DONE
alias cck.GoodChans.Add {
  var %nc = $s2c($1,$2)
  if ($hfind(cck.HGoodChans,%nc,1,W)) { cck.echo/ GOODCHANS:12 %nc Already exists (wildcard match). | return EXIST }
  hadd cck.HGoodChans %nc 0 Never | hsave cck.HGoodChans $qt($hget(cck.HDisk,GoodChans))
  cck.diaMAIN.upd GoodChans ADD %nc
  cck.echo+ GOODCHANS:12 %nc Added. | return DONE
}

; +++ GOOD CHANNELS / REMOVE
; IN32 $1 = network or * $2 = channel
; OUT32 $1 = NOTFOUND or DONE
alias cck.GoodChans.Rem {
  var %nc = $s2c($1,$2)
  if (!$hfind(cck.HGoodChans,%nc,1)) { cck.echo- GOODCHANS:12 %nc Not found. | return NOTFOUND }
  hdel cck.HGoodChans %nc | hsave cck.HGoodChans $qt($hget(cck.HDisk,GoodChans))
  cck.diaMAIN.upd GoodChans REM %nc
  cck.echo+ GOODCHANS:12 %nc Removed. | return DONE
}

; +++ GOOD CHANNELS / CHECK IF GIVEN CHANNEL IS MATCH
; IN32 $1 = network $2 = channel
; OUT32 $1 = 1 (match) or 0 (no match)
alias cck.GoodChans.Check {
  var %nc = $s2c($1,$2) | if ($hfind(cck.HGoodChans,%nc,1,W) == $null) { return 0 }
  var %nc = $v1 | tokenize 32 $hget(cck.HGoodChans,%nc)
  hadd cck.HGoodChans %nc $calc($1 + 1) $ctime | hsave cck.HGoodChans $qt($hget(cck.HDisk,GoodChans))
  cck.diaMAIN.upd GoodChans REP %nc | return 1
}

; GOOD CHANNELS / SET DEFAULT
alias cck.GoodChans.Default {
  .remove $qt($hget(cck.HDisk,GoodChans)) | cck.GoodChans.Load | cck.diaMAIN.load GoodChans
}

;===================================================================================================
; CONFIG / TRACK USERS
;===================================================================================================

; +++ TRACK USERS / LOAD
alias cck.TrackUsers.Load {
  var %h = cck.HTrackUsers | if ($hget(%h)) { hfree %h } | hmake %h 30
  var %f = $hget(cck.HDisk,TrackUsers) | if ($isfile(%f)) { hload %h $qt(%f) }
  var %c = cck.trackusers.load1
  ; *****

  ; Example: next rule set has tag 'Default1' and will match ppl that:
  ; - Are operator on a channel named '#screwyou' (INCLUDE,WILDCARD)
  ; - AND have 'fuck' inside their real name (INCLUDE,WILDCARD)
  ; - AND are NOT authed (EXclude,Wildcard * - matches any authname)
  ; %c Default1,IN1W,PCHANNEL @#screwyou
  ; %c Default1,IN1W,REALNAME *fuck*
  ; %c Default1,EX1W,AUTHNAME *

  ; - When detected perform ACT: ASK or KICK or BAN
  ; %c Default1,ACT ASK
  ; %c Default1,KICKMSG Look like a lamer? Treated like a lamer.

  ; Example: next rule has tag 'Default2' and will match ppl that:
  ; - Have their real name starting with 'you suck' (INCLUDE,WILDCARD)
  ; - AND NOT on the irc networks  UnderNet and QuakeNet
  ; %c Default2,IN1W,REALNAME you suck*
  ; %c Default2,EX1W,NETWORK UnderNet
  ; %c Default2,EX2W,NETWORK QuakeNet

  ; *****
  hsave %h $qt(%f)
}
alias -l cck.trackusers.load1 { if ($hfind(cck.HTrackUsers,$1,1) == $null) { hadd cck.HTrackUsers $1- } }

; the ^ prefix in some parameters indicate that they need SpecialWhois to retrieve.
; others may only be showed in own whois or may require IRC operator privileges
alias cck.TrackUsers.Pars {
  return NETWORK NICK USER HOST MASK REALHOST USERIP REALNAME REGNICK AUTHNAME PCHANNEL MODES $&
    AWAY ^LOGON ^IDLE SERVHOST SERVINFO BOT SERVICE HELPOP IRCOP SSL FILTERING IMMUNETOFILTERING CALLERID SOFTCALLERID CGI:IRC ACT KICKMSG
}

; +++ TRACK USERS / ADD RULE
; IN32 $1 = tag $2 = INW or INR or EXW or EXR $3 = parameter $4- = value
; OUT32: $1 = INVALID or EXIST or DONE
alias cck.TrackUsers.AddRule {
  var %4 = $chr(44)
  if ($1 !isalnum) || (!$1) || ($len($1) > 8) {
    cck.echo- TRACKUSERS: $+(04,$1,12,%4,$2,%4,$3,) Tag allows a-zA-Z0-9 characters only, $&
      cannot be only zeroes, and should be max 8 characters.
    return INVALID
  }
  var %h = cck.HTrackUsers, %hi, %i = 1
  while ($hfind(%h,$+($1,%4,$left($2,2),%i,$right($2,1),%4,$3),1)) {
    %hi = $v1
    if ($hget(%h,%hi) === $4-) { cck.echo/ TRACKUSERS:12 %hi Rule already exists with value $qt($4-) $+ . | return EXIST }
    inc %i
  }
  var %hi = $+($1,%4,$left($2,2),%i,$right($2,1),%4,$3)
  hadd %h %hi $4- | hsave %h $qt($hget(cck.HDisk,TrackUsers))
  cck.diaMAIN.upd TrackUsers ADD %hi
  cck.echo+ TRACKUSERS:12 %hi Rule added (value $qt($4-) $+ )
  if ($istok(^LOGON ^IDLE,$3,32)) && ($hget(cck.HConfig,SpecialWhois) == OFF) {
    cck.echo/ TRACKUSERS:12 %hi SpecialWhois needs to be ON for ^LOGON and ^IDLE. )
  }
  return DONE
}

; +++ TRACK USERS / REMOVE RULE
; IN32 $1 = tag $2 = IN<N>W or IN<N>R or EX<N>W or EX<N>R $3 = parameter
; OUT32 $1 = NOTFOUND or DONE
alias cck.TrackUsers.RemRule {
  var %hi = $s2c($1,$2,$3), %h = cck.HTrackUsers
  if (!$hfind(%h,%hi,1)) { cck.echo- TRACKUSERS:12 %hi Rule not found. | return NOTFOUND }
  var %hv = $hget(%h,%hi) | hdel %h %hi | hsave %h $qt($hget(cck.HDisk,TrackUsers))
  cck.diaMAIN.upd TrackUsers REM %hi
  cck.echo+ TRACKUSERS:12 %hi Rule removed (value $qt(%hv) $+ ).
  return DONE
}

; +++ TRACK USERS / ADD-CHANGE CONFIG
; IN32 $1 = tag $2 = ACT or KICKMSG or  DO/LIST
; case ACT: $3- = ASK or KICK or BAN
; case KICKMSG: $3- = kick message
; OUT32: $1 = INVALID or EXIST or DONE
alias cck.TrackUsers.AddConfig {
  var %4 = $chr(44)
  if ($1 !isalnum) || (!$1) || ($len($1) > 8) {
    cck.echo- TRACKUSERS: $+(04,$1,,%4,12,$2,) Tag allows a-zA-Z0-9 characters only, $&
      cannot be only zeroes, and should be max 8 characters.
    return INVALID
  }
  if ($2 == ACT) {
    if (!$istok(ASK KICK BAN,$3-,32)) {
      cck.echo- TRACKUSERS: $+(12,$1,%4,ACT) Config has invalid value04 $3- $+ . Should be ASK, KICK or BAN.
      return INVALID
    }
  }
  var %hi = $+($1,%4,$2), %h = cck.HTrackUsers
  if ($hfind(%h,%hi,1)) {
    if ($2 == DO/LIST) { cck.echo/ TRACKUSERS:12 %hi Config already set. | return EXIST }
    if ($hget(%h,$v1) === $3-) { cck.echo/ TRACKUSERS:12 %hi Config already set to $qt($3-) $+ . | return EXIST }
    hdel %h %hi | cck.diaMAIN.upd TrackUsers REM %hi
  }
  hadd %h %hi $3- | cck.diaMAIN.upd TrackUsers ADD %hi
  hsave %h $qt($hget(cck.HDisk,TrackUsers))
  cck.echo+ TRACKUSERS:12 %hi $+  $iif($2 ==  DO/LIST,Config set.,Config set to $qt($3-) $+ .) | return DONE
}

; +++ TRACK USERS / REMOVE CONFIG
; IN32 $1 = tag $2 = ACT or KICKMSG or  DO/LIST
; OUT32 $1 = NOTFOUND or DONE
alias cck.TrackUsers.RemConfig {
  var %hi = $s2c($1,$2), %h = cck.HTrackUsers
  if (!$hfind(%h,%hi,1)) { cck.echo- TRACKUSERS:12 %hi Not found. | return NOTFOUND }
  hdel %h %hi | hsave %h $qt($hget(cck.HDisk,TrackUsers))
  cck.diaMAIN.upd TrackUsers REM %hi
  cck.echo+ TRACKUSERS:12 %hi Removed. | return DONE
}

; +++ TRACK USERS / CHECK
; IN32 $1 = hash name, layout parameter=value $2 = @window name, layout <prefixedchannel>
; OUT $1 = 0 (no match) or $1 = ASK or KICK or BAN $2 = first matching tag $3 = DO/LIST or NO/LIST $4- = kickmsg
alias cck.TrackUsers.Check {
  var %hin = $1, %win = $2, %h = cck.HTrackUsers, %hr = $rhash, %hi, %i1 = 1, %i2, %tag, %s, %4 = $chr(44)
  while ($hget(%h,%i1).item) { hadd %hr $gettok($v1,1,44) 0 | inc %i1 }

  %i1 = 1
  :nextTAG
  %tag = $hget(%hr,%i1).item | if (%tag == $null) { goto wipeIN }
  %i2 = 1

  ; wipe tags with Exclude matches
  :nextEX
  %hi = $hfind(%h,$+(%tag,%4,EX*,%4,*),%i2,w) | if (%hi == $null) { %i2 = 1 | goto nextIN }
  tokenize 44 %hi | %s = $right($2,1)
  if ($3 == PCHANNEL) { if ($fline(%win,$hget(%h,%hi),1,$iif(%s == R,2,w))) { hdel %hr %tag | goto nextTAG } }
  elseif (%s == W) { if ($hget(%h,%hi) iswm $hget(%hin,$3)) { hdel %hr %tag | goto nextTAG } }
  elseif (%s == R) { if ($regex($hget(%hin,$3),$hget(%h,%hi))) { hdel %hr %tag | goto nextTAG } }
  inc %i2 | goto nextEX

  ; count Include matches per tag
  :nextIN
  %hi = $hfind(%h,$+(%tag,%4,IN*,%4,*),%i2,w) | if (%hi == $null) { inc %i1 | goto nextTAG }
  tokenize 44 %hi | %s = $right($2,1)
  if ($3 == PCHANNEL) { if ($fline(%win,$hget(%h,%hi),1,$iif(%s == R,2,w))) { hinc %hr %tag } }  
  elseif (%s == W) { if ($hget(%h,%hi) iswm $hget(%hin,$3)) { hinc %hr %tag } }
  elseif (%s == R) { if ($regex($hget(%hin,$3),$hget(%h,%hi))) { hinc %hr %tag } }
  inc %i2 | goto nextIN

  ; wipe tags with no full Includes match
  :wipeIN
  %i1 = 1
  while ($hget(%hr,%i1).item) {
    %hi = $v1
    if ($hfind(%h,$+(%hi,%4,IN*,%4,*),0,w) > $hget(%hr,%hi)) { hdel %hr %hi } | else { inc %i1 }
  }
  %hi = $hget(%hr,1).item | if (%hi == $null) { hfree %hr | return 0 }
  %tag = $gettok(%hi,1,44) | %i = 2 | %s = %tag
  while ($hget(%hr,%i).item) { %s = %s $gettok($v1,1,44) | inc %i }
  hfree %hr
  if ($numtok(%s,32) > 1) { cck.echo/ TRACKUSERS: Detected overlapping RuleSets (12 $+ %s $+ )  }
  %s = $iif($hget(cck.HConfig,TrackUsers.DefaultDo/List) == ON,DO/LIST,NO/LIST)
  return $iif($hget(%h,$+(%tag,%4,ACT)),$v1,$hget(cck.HConfig,TrackUsers.DefaultAct)) %tag $&
    $iif($hfind(%h,$+(%tag,%4,DO/LIST),1),DO/LIST,%s) $&
    $iif($hget(%h,$+(%tag,%4,KICKMSG)),$v1,$hget(cck.HConfig,TrackUsers.DefaultKickMsg))
}
alias cck.TrackUsers.Default { .remove $qt($hget(cck.HDisk,TrackUsers)) | cck.TrackUsers.Load }

;===================================================================================================
; CONFIG / BANS
;===================================================================================================

; <ctime unban|PERM> <network> <channel> <mask> <reason>

; +++ BANS / ADD
; IN32 $1 = network $2 = channel $3 = mask $4 = duration in sec $5- = reason
; OUT32: $1 = EXIST or DONE
alias cck.Bans.Add {
  var %f = $hget(cck.HDisk,Bans), %utime = $calc($ctime + $4), %s
  if ($read(%f,w,& $1 $2 $3 *)) {
    var %rec = $v1
    if ($gettok(%rec,1,32) == PERM) || ($v1 > %utime) { cck.echo/ BANS:12 $1-3 Already exists with equal or longer period. | return EXIST }
    write -l $readn $qt(%f) %utime $1-3 $5- | %s = Updated
  }
  else {
    write $qt(%f) %utime $1-3 $5-
    cck.queue.add $1 MODE $2 +b $3 CHECKCHANS-BANS
    cck.kickclones $1 $2 $3 $5-
    %s = Added
  }
  filter -ffctu 1 32 $qt(%f) $qt(%f) *
  cck.diaMAIN.upd Bans ADD $1-3
  cck.echo+ BANS:12 $1-3  $+ %s for $duration($4) ( $+ $5- $+ ). | return DONE
}

; +++ BANS / REMOVE
; IN32 $1 = network $2 = channel $3 = mask
; OUT32 $1 = NOTFOUND or DONE
alias cck.Bans.Rem {
  var %f = $hget(cck.HDisk,Bans)
  if (!$read(%f,w,& $1 $2 $3 *)) { cck.echo/ BANS:12 $1-3 Not found. | return NOTFOUND }
  write -dl $+ $readn $qt(%f)
  cck.diaMAIN.upd Bans REM $1-3
  cck.echo+ BANS:12 $1-3 Removed. | return DONE
}

; +++ BANS / CLEAN
; IN32 $1 = network $2 = channel
alias cck.Bans.Clean {
  var %f = $hget(cck.HDisk,Bans) | if (!$isfile(%f)) { return }
  var %n = $1, %c = $2, %w = $rwin, %i = 1, %rec, %cid, %wiped = 0
  filter -fw $qt(%f) %w *
  while ($line(%w,%i)) {
    tokenize 32 $v1 | if ($2 != %n) { inc %i | continue }
    %cid = $cck.net2cid($2) | if (!%cid) { inc %i | continue }
    if (%cid != $cid) { scid %cid }
    if ($4 !isban %c) { dline %w %i | inc %wiped } | else { inc %i }
  }
  if (%wiped) { filter -wfc %w $qt(%f) * | cck.diaMAIN.load Bans }
  window -c %w
}

; +++ BANS / CHECK USER
; IN32 $1 = network $2 = channel $3 = mask
; OUT32 $1 = 1 (match) or 0 (no match)
alias cck.Bans.CheckUser {
  var %f = $hget(cck.HDisk,Bans) | if (!$isfile(%f)) { return 0 }
  var %w = $rwin, %i = 1 | filter -fw $qt(%f) %w & $1 $2 *
  while ($line(%w,%i)) { if ($gettok($v1,4,32) iswm $3) { window -c %w | return 1 } | inc %i }
  window -c %w | return 0
}

; +++ BANS / SCAN UNBAN
alias cck.Bans.ScanUnban {
  var %f = $hget(cck.HDisk,Bans) | if (!$isfile(%f)) { return }
  var %w = $rwin, %i = 1, %rec
  filter -fw $qt(%f) %w *
  while ($line(%w,%i)) {
    tokenize 32 $v1
    if ($1 == PERM) || ($1 > $ctime) { inc %i | continue }
    cck.queue.add $2 MODE $3 -b $4 CHECKCHANS-BANS
    inc %i
  }
  window -c %w
}

;===================================================================================================
; HELP
;===================================================================================================

; +++ HELP WINDOW
alias cck.help {
  var %w = @CCK.Help | if ($window(%w)) { window -a %w | return }
  window -da %w -1 -1 $hget(cck.HConfig,WinXY) | var %c = aline -pi4 %w
  %c $+(,$hget(cck.HConfig,VersionLong),)
  %c -
  %c INTRODUCTION
  %c This script deals with that are on considered-bad channels or have considered-bad text in their IRC data.
  %c -
  %c WORKING PRINCIPLE
  %c To check users, the script sends WHOIS commands to the irc server.
  %c This can be started in 5 ways:
  %c - Manually: using the scripts interface.
  %c - At User Join: the moment the user joins your detection channels.
  %c - At Own Join: at the moment you join, script checks all users.
  %c - By Timers: every X secs, script will check everybody on the detection channels and/or predefined nicks.
  %c - Continue: same as Own Join, but the end of the all-check immediately starts a next all-check
  %c Upon detection, 4 actions are available: ASK, WARN, KICK, BAN (the latter also kicks).
  %c In case ASK, the script will wait to take action and allow you to decide which of the 3 actions it has to perform.
  %c This can be selected in a dialog and by 3 configurable Function Keys (ex. SF10 = Shift-F10)
  %c -
  %c The channelnames of the users are matched in two ways, either exact, either the presence of a word in it.
  %c The exact way is named '04BadChans', the word presence way is named '07BadStrings'.
  %c Above actions are separately configurable for these two ways.
  %c So you can for ex. automatically BAN users in case 04BadChans match, and ASK in case 07BadStrings match.
  %c This approach allows you to add suspected words, giving you the chance to find out if the channel is really bad, before taking actions.
  %c If this BadStrings matching channel should not be considered bad, it can be added to the '03GoodChans' list.
  %c So this '03GoodChans' list acts as exclude list for the '07BadStrings' list.
  %c -
  %c The WARN and KICK message can be configured, and supports the tags <BADCHANS> and <MYCHANS>.
  %c These tags will be replaced by the script with the found bad channel list and the channel(s) the check happened.
  %c You can exclude channel Operators/Halfops/Voices and a list of nicks (mainly for networks' services) from checking.
  %c Keep in mind that it's still possible that the script checks these users since there is a delay between their join and mode.
  %c Also, if you check a channel that isn't in CheckChans, nobody except the list of nicks will be excluded and also no action will take place.
  %c -
  %c 04Track Users
  %c The script can also detect and act on specific users (or 'kind' of users) with dedicated Rule Sets, that are applied on the WHOIS results.
  %c These rules consist of Include Rules (all have to match) and Exclude Rules (none should match).
  %c So, besides bad channels, the script can also do bad <anything in WHOIS> checks.
  %c The Rules Sets are grouped by the <tag> field. There are:
  %c *** 2 types rules IN(clude)/EX(clude).
  %c *** 2 types matchstring W(ildcard)/R(egex).
  %c *** 2 Configuration items ACT/KICKMSG
  %c -
  %c INCLUDE: <tag>,<IN<N>W|IN<N>R>,<parameter> = <value of par>
  %c INW = INclude rule and Wildcard match text
  %c INR = INclude rule and Regular expression match text
  %c -
  %c EXCLUDE: <tag>,<EX<N>W|EX<N>R>,<parameter> = <value of par>
  %c EXW = EXclude rule and Wildcard match text
  %c EXR = EXclude rule and Regular expression match text
  %c A certain <tag> can have Multiple Include and Exclude Rules for the same parameter (but different value).
  %c The script will auto-insert a sequential number in the Rule Types field.
  %c A Rule Set has some other 'member' entries available:
  %c - ACT: <tag>,ACT = ASK|KICK|BAN
  %c - KICKMSG: <tag>,KICKMSG = text
  %c - DO/LIST: <tag>,DO/LIST = n/a (do a /list on all the channels returned for a matching user)
  %c -
  %c 04Check Nicks
  %c This is a list of <network> <nicks> that is scanned independently of channel presence.
  %c A <network> named '*' (asterisk) is used to indicate you want it to work on all networks you are present on.
  %c Its purpose is to scan these nicks at a high rate, in order to find out how their whois data changes over time.
  %c Parameters:
  %c - Status: enable/disable the checking of the nick. It's datafile remains, unlike when removing the nick from the list.
  %c - Interval:
  %c The 'default' interval of scanning is named 'SweepNick'.  It's also possible to set intervals on a per nick basis.
  %c Another timer, named 'TimePolling' determines the accuracy of these per nick intervals.
  %c The lower the interval you let it 'poll', the higher the accuracy, but the more work for mIRC,
  %c since it has to dwell through the CheckNicks list everytime, to compare time values of all non-'default' settings.
  %c It should certainly be much lower than the lowest per nick interval.
  %c Ex. if you want to check a nick every 10 sec then a polling interval of 1 sec can already cause a 10% deviation.
  %c When alot nicks in the list it's recommended to use some different intervals, to spread out and thus decrease the queue command supply peeks.
  %c - Upon nickchange, 4 possible settings:
  %c    Ignore = do nothing.
  %c    Log = only store the new nick in the data file of the old nick.
  %c    Update = change the original nick in the CheckNicks list. This will effectively remove the old nick from the list.
  %c    Add = add the new nick to the CheckNicks list. Both nicks will now be checked.
  %c - Hunt: this will attempt to keep a common channel with the nick
  %c - MinUsers: the required amount users on a channel to be candidate for huntchannel.
  %c - OnlyRegular: if the nick is not regular on the channel (so op/voice/etc) then the channel is no candidate for huntchannel.
  %c - GlobalUpdates: if ON then the script will perform the following global data updates on this entry:
  %c    1. When you join a channel that is in the data, the amount users will be updated.
  %c    2. At JOIN/PART/NICK/QUIT events, when the users HOST is in the data, the users (old/new)NICK(s) will be added.
  %c - AllNicks: if OFF then the script will /whois only the entry nick, if ON then it will /whois all found nicks in the data.
  %c Some global settings:
  %c - ChanFlagHistory: determines the amount changes in op/voice/regular/etc that is stored per channel.
  %c - StopWhenIRCOP: will switch off network,nick entries that are detected as IRCOP or SERVICE. Also if the network is * (any).
  %c - MaxNicksToWhois: the script will /whois at max this amount found nicks per CheckNicks entry. It will take the most recent ones.
  %c -
  %c NOTES
  %c To make mIRC not suffer hangs, a DeadGap function is provided, which will insert a delay (in milliseconds) between user checks. 
  %c The bigger you make this delay, the longer it will take to complete the check, so people on bad channels might be able to stay in for a while.
  %c But the lower you make it, the less mIRC is able to cope with other things, causing a higher risk of having periods of unresponsivity.
  %c If setting UsePING is ON then the script will send a PING after commands that are not answered by the IRC server.
  %c These commands are MSG NOTICE DESCRIBE CTCP and channel-user MODE's b, e or i (so an address mask as parameter)
  %c This allows eventdriven queue processing just like commands that do get IRC server responses.
  %c Drawback of this method is that it doubles the amount commands to the IRC server and thus cause more lag.
  %c To allow to disable UsePING without introducing a FloodDisconnect-risk , an alternate delay setting for DeadGap timer is provided.
  %c When UsePING is OFF, this second delay, named NoPINGGAP (also milliseconds), will be used for the mentioned commands.
  %c A setting of at least 2000 is recommended. If you see the script cause FloodDisconnect, increase it.
  %c I kept this setting in milliseconds to still allow a low setting for privileged IRC server users that have higher or no limits.
  %c Another setting Timeout in seconds avoids queue stalls/user intervention needed, when something in the eventdrive goes wrong.
}

;===================================================================================================
; MENU POPUPS / COMMANDLINE
;===================================================================================================
menu status,channel,query,menubar,status,@CCK.* {
  .$hget(cck.HConfig,VersionShort)
  .#
  ..WHOIS: { cck.whoischan $$network $$chan CHECKCHANS-UIL }
  ..WHOIS + LIST: { cck.whoischan $$network $$chan CHECKCHANS-UIL;LIST }
  ..-
  ..Add to CheckChans: { cck.CheckChans.Add $$network $$chan OFF OFF OFF OFF }
  ..$str($chr(160),3) $+ UserJoin ( $+ $cck.CheckChans.Get($network,$chan,USERJOIN) $+ )
  ...ON: { cck.CheckChans.Change $$network $$chan USERJOIN ON }
  ...OFF: { cck.CheckChans.Change $$network $$chan USERJOIN OFF }
  ..$str($chr(160),3) $+ OwnJoin ( $+ $cck.CheckChans.Get($network,$chan,OWNJOIN) $+ )
  ...ON: { cck.CheckChans.Change $$network $$chan OWNJOIN ON }
  ...OFF: { cck.CheckChans.Change $$network $$chan OWNJOIN OFF }
  ..$str($chr(160),3) $+ Timer ( $+ $cck.CheckChans.Get($network,$chan,TIMER) $+ )
  ...ON: { cck.CheckChans.Change $$network $$chan TIMER ON }
  ...OFF: { cck.CheckChans.Change $$network $$chan TIMER OFF }
  ..$str($chr(160),3) $+ Continue ( $+ $cck.CheckChans.Get($network,$chan,CONTINUE) $+ )
  ...ON: { cck.CheckChans.Change $$network $$chan CONTINUE ON }
  ...OFF: { cck.CheckChans.Change $$network $$chan CONTINUE OFF }
  ..-
  ..Remove: { cck.CheckChans.Rem $$network $$chan }
  .-
  .Dialog MAIN: { cck.diaMAIN 1 }
  .$str($chr(160),3) $+ CheckChans / Config1: { cck.diaMAIN 1 }
  .$str($chr(160),3) $+ BadChans / Config2: { cck.diaMAIN 2 }
  .$str($chr(160),3) $+ BadStrings / GoodChans: { cck.diaMAIN 3 }
  .$str($chr(160),3) $+ TrackUsers: { cck.diaMAIN 4 }
  .$str($chr(160),3) $+ CheckNicks: { cck.diaMAIN 5 }
  .$str($chr(160),3) $+ Bans: { cck.diaMAIN 6 }
  .Dialog ASK: { cck.diaASK } 
  .-
  .Set Function Keys: { cck.key.config }
  .Command Queue ( $+ $cck.queue.get#(*,*) $+ )
  ..Process ( $+ $cck.queue.process $+ )
  ...ON: { cck.queue.process ON }
  ...OFF: { cck.queue.process OFF }
  ..Recover Stall: { cck.queue.recover }
  ..Flush: { cck.queue.remnet * }
  .( Help ): { cck.help }
}
menu nicklist {
  .$hget(cck.HConfig,VersionShort)
  ..WHOIS Selected: { var %i = 1 | while ($snick(#,%i)) { cck.whois $v1 | inc %i } }
  ..WHOIS+LIST Selected: { var %i = 1 | while ($snick(#,%i)) { cck.list $v1 | inc %i } }
  ..Add to CheckNicks ( $+ $network $+ ): { var %i = 1 | while ($snick(#,%i)) { cck.CheckNicks.Add $network $v1 default NOHUNT LOG LUI ON 15 ON ON ON | inc %i } }
  ..Add to CheckNicks (any net): { var %i = 1 | while ($snick(#,%i)) { cck.CheckNicks.Add * $v1 default NOHUNT LOG LUI ON 15 ON ON ON | inc %i } }
}
menu @CCK.* {
  .-
  .List Selected: {
    var %i = 1, %nick = $gettok($active,2,44)
    while ($sline($active,%i) != $null) { tokenize 9 $v1 | cck.queue.add $1 LIST $4 - - $+(CHECKNICKS-UIL,;,%nick,;?;,%nick) | inc %i }
  }
  .-
  .Copy Column 4: {
    clipboard
    if ($sline($active,0) == 1) { clipboard $strip($gettok($sline($active,1),4,9)) }
    else { var %i = 1 | while ($sline($active,%i) != $null) { clipboard -an $gettok($v1,4,9) | inc %i } }
  }
  .Copy Line(s): {
    clipboard
    if ($sline($active,0) == 1) { clipboard $sline($active,1) } | else { var %i = 1 | while ($sline($active,%i) != $null) { clipboard -an $v1 | inc %i } }
  }
  .Copy Line(s)+Strip: {
    clipboard
    if ($sline($active,0) == 1) { clipboard $strip($sline($active,1)) } | else { var %i = 1 | while ($sline($active,%i) != $null) { clipboard -an $v1 | inc %i } }
  }
  .-
  .Delete Line(s): { while ($sline($active,1).ln) { cck.CheckNicks.DeleteDataLine $active $v1 } }
}
alias cck.whois { cck.queue.add $network WHOIS $$1 - $iif($address($1,0),$gettok($v1,2-,33),-) UIL }
alias cck.list { cck.queue.add $network WHOIS $$1 - $iif($address($1,0),$gettok($v1,2-,33),-) UIL;LIST }

;===================================================================================================
; DIALOG FUNCTION BASE
;===================================================================================================

; +++ DIALOG FUNCTIONS
alias cck.dia.RepSW { if ($did($1,$2,$3).state) { return -ockz } | return -okz }
alias cck.dia.CheckEdit { var %i = 2 | while (%i <= $0) { if ($did($1,$ [ $+ [ %i ] ]) == $null) { return 0 } | inc %i } | return 1 }
alias cck.dia.CheckCombo { var %i = 2 | while (%i <= $0) { if ($did($1,$ [ $+ [ %i ] ],0) == $null) { return 0 } | inc %i } | return 1 }
alias cck.dia.OOsw { if ($1 == ON) { return -c } | return -u }
alias cck.dia.swOO {
  var %i = 2, %out
  while ($ [ $+ [ %i ] ] != $null) { %out = %out $iif($did($1,$v1).state,ON,OFF) | inc %i }
  return %out
}

; Select all listbox lines matching token on given position
; Input: $1 = dialog $2 = listid $3 = token position $4 = ascii token separator $5 = P or S or PS (wildcard * prefix/suffix-optional)
alias cck.dia.ListSelTokSame {
  var %d = $1, %id = $2, %pos = $3, %sep = $4, %ps = $5
  var %totalsel = $did(%d,%id,0).sel, %i = 1, %w = $rwin
  while (%i <= %totalsel) { var %l = $did(%d,%id,%i).sel | aline %w $gettok($did(%d,%id,%l),%pos,%sep) | inc %i }
  var %total = $did(%d,%id).lines, %i1 = 1
  while (%i1 <= %totalsel) {
    var %rec = $line(%w,%i1) | if (%ps == P) { var %rec = $+(*,%rec) } | elseif (%ps == S) { var %rec = $+(%rec,*) } | elseif (%ps == PS) { var %rec = $+(*,%rec,*) }
    var %i2 = 1 | while (%i2 <= %total) { if (%rec iswm $gettok($did(%d,%id,%i2),%pos,%sep)) { did -ck %d %id %i2 } | inc %i2 }
    inc %i1
  }
  window -c %w
}
; Select all listbox lines with given token on given position
; IN32 $1 = dialog $2 = listid $3 = token position $4 = ascii token separator $5 = token value
; OUT $1 = amount selected
alias cck.dia.ListSelTokVal { var %i = 1 | while ($did($1,$2,%i) != $null) { if ($gettok($v1,$3,$4) == $5) { did -ck $1 $2 %i } | inc %i } }

;===================================================================================================
; DIALOG MAIN
;===================================================================================================

; +++ DIALOG MAIN / TABLE
dialog CCK.MAIN {
  title ""
  size -1 -1 276 168
  option dbu
  tab "1.Check Channels / Configuration1", 1, 1 -2 273 168
  list 11, 3 13 123 128, tab 1 sort size extsel hsbar vsbar
  check "UserJoin", 13, 4 142 26 10, tab 1 push
  edit "", 12, 3 153 123 10, tab 1 autohs
  check "OwnJoin", 14, 31 142 25 10, tab 1 push
  check "By Timer", 15, 57 142 25 10, tab 1 push
  button "Add", 17, 127 153 16 11, tab 1
  button "Rem", 16, 127 129 16 11, tab 1
  text "ActBadStrings", 18, 171 14 39 8, tab 1
  text "ActBadChannels", 20, 129 14 41 8, tab 1
  button "Set", 29, 259 153 13 11, tab 1
  edit "", 22, 127 42 146 31, tab 1 multi autovs vsbar
  text "WARN Message (tags: <BADCHANS> <MYCHANS>)", 23, 128 33 133 8, tab 1
  text "KICK Message (tags: <BADCHANS> <MYCHANS>)", 24, 128 74 133 8, tab 1
  edit "", 25, 127 83 146 31, tab 1 multi autovs vsbar
  combo 21, 128 21 41 42, tab 1 drop
  combo 19, 170 21 40 42, tab 1 drop
  edit "", 75, 240 143 17 10, tab 1 limit 3
  edit "", 74, 240 133 17 10, tab 1 limit 3
  edit "", 72, 240 123 17 10, tab 1 limit 4
  text "MaxDataLength", 73, 220 115 39 8, tab 1
  text "ASK", 76, 220 125 19 8, tab 1
  text "WARN", 77, 220 135 19 8, tab 1
  text "KICK", 78, 220 145 19 8, tab 1
  edit "", 80, 240 153 17 10, tab 1 limit 4
  text "LOG", 79, 220 155 19 8, tab 1
  check "Continue", 81, 83 142 26 10, tab 1 push
  button "", 91, 261 15 7 7, hide tab 1 ok
  button "Join", 106, 127 117 14 11, tab 1
  button "Part", 107, 142 117 14 11, tab 1
  tab "2.Bad Channels / Configuration2", 2
  list 34, 3 13 123 128, tab 2 sort size extsel hsbar vsbar
  edit "", 35, 3 153 123 10, tab 2 autohs
  button "Add", 36, 91 142 16 11, tab 2
  button "Rem", 37, 109 142 16 11, tab 2
  text "BAD CHANNELS", 47, 4 143 44 8, tab 2
  box "QUEUE + TIME SETTINGS", 46, 128 14 144 96, tab 2
  button "Set", 68, 259 153 13 11, tab 2
  edit "", 62, 180 98 87 10, tab 2
  text "SweepChan Interval", 63, 132 35 51 8, tab 2
  text "Dead Gap Duration", 64, 132 68 47 8, tab 2
  edit "", 60, 184 33 20 10, tab 2
  edit "", 61, 184 66 25 10, tab 2
  text "sec.", 65, 205 35 12 8, tab 2
  text "msec.", 66, 210 68 15 8, tab 2
  text "TimeFormat", 67, 132 100 46 8, tab 2
  check "ShowNonMatches", 71, 222 111 49 10, tab 2 push
  check "@ Operators", 27, 132 127 35 10, tab 2 push
  box "Exclude from being checked", 30, 128 119 144 32, tab 2
  text "Nicks", 32, 252 131 16 7, tab 2
  check "+ Voices", 28, 198 127 26 10, tab 2 push
  check "% HalfOps", 26, 168 127 29 10, tab 2 push
  edit "", 31, 131 138 138 10, tab 2
  check "Don't abort check at PART/KICK", 33, 128 153 84 10, tab 2 push
  check "DetailedWHOIS", 70, 213 153 42 10, tab 2 push
  check "Monitor Queue", 96, 132 22 40 10, tab 2 push
  button "Flush", 97, 197 22 16 10, tab 2
  check "Process", 98, 173 22 23 10, tab 2 push
  check "UsePING", 99, 132 76 27 10, tab 2 push
  edit "", 100, 216 76 25 10, tab 2
  text "msec.", 101, 242 78 15 8, tab 2
  text "NoPING Gap Duration", 102, 161 78 55 8, tab 2
  text "Overall Timeout", 105, 132 89 39 8, tab 2
  edit "", 103, 172 87 20 10, tab 2
  text "sec.", 104, 193 89 15 8, tab 2
  button "Stall Recover", 112, 214 22 36 10, tab 2
  text "SweepNick Interval", 113, 132 46 51 8, tab 2
  edit "", 114, 184 44 20 10, tab 2
  text "sec.", 115, 205 46 12 8, tab 2
  text "TimePolling Interval", 128, 132 57 51 8, tab 2
  edit "", 126, 184 55 20 10, tab 2
  text "sec.", 127, 205 57 12 8, tab 2
  tab "3.Bad Strings / Good Channels", 3
  list 38, 3 13 123 128, tab 3 sort size extsel hsbar vsbar
  edit "", 39, 3 153 123 10, tab 3
  button "Add", 40, 91 142 16 11, tab 3
  button "Rem", 41, 109 142 16 11, tab 3
  list 42, 149 13 123 128, tab 3 sort size extsel hsbar vsbar
  edit "", 45, 149 153 123 10, tab 3
  button "Add", 43, 237 142 16 11, tab 3
  button "Rem", 44, 255 142 16 11, tab 3
  text "BAD STRINGS", 48, 4 143 38 8, tab 3
  text "GOOD CHANNELS", 49, 150 143 48 8, tab 3
  text "SWEEP", 50, 128 46 21 8, tab 3
  button "Set", 59, 131 116 13 11, tab 3
  text "BAN", 53, 131 106 13 8, tab 3
  edit "", 58, 128 96 19 10, tab 3 limit 4
  text "KICK", 52, 131 86 14 8, tab 3
  edit "", 57, 128 76 19 10, tab 3 limit 4
  text "WARN", 51, 129 66 18 8, tab 3
  edit "", 56, 128 56 19 10, tab 3 limit 4
  edit "", 55, 128 36 19 10, tab 3 limit 4
  text "F-KEYS", 54, 128 27 21 8, tab 3
  button "Sound", 69, 128 14 19 11, tab 3
  tab "4.Track Users", 4
  list 5, 3 13 245 107, tab 4 sort size extsel hsbar vsbar
  button "Add", 7, 249 121 14 11, tab 4
  button "Remove", 8, 249 108 24 11, tab 4
  edit "", 10, 33 142 215 10, tab 4
  button "SetDefault", 85, 31 153 29 11, tab 4
  combo 87, 79 121 47 90, tab 4 vsbar drop
  combo 88, 4 142 28 36, tab 4 drop
  button "Sel Tag", 90, 249 96 24 11, tab 4
  edit "", 92, 3 121 50 10, tab 4
  text "Wildcard Text / Regex", 95, 192 131 56 8, tab 4
  text "Tag of Rules Set", 93, 10 131 42 8, tab 4
  text "KICK Message", 94, 182 152 37 8, tab 4
  button "SetDefault", 82, 236 153 29 11, tab 4
  edit "", 83, 126 121 122 10, tab 4 autohs
  text "Parameter", 84, 92 132 26 8, tab 4
  text "ACT", 86, 4 153 11 8, tab 4
  combo 6, 54 121 24 43, tab 4 drop
  text "Type", 9, 56 132 15 8, tab 4
  button "Add", 89, 221 153 14 11, tab 4
  check "DO/LIST", 108, 92 153 26 10, tab 4 push
  button "SetDefault", 109, 134 153 29 11, tab 4
  button "Add", 111, 16 153 14 11, tab 4
  button "Add", 110, 119 153 14 11, tab 4
  tab "5.Check Nicks", 116
  list 117, 3 13 213 127, tab 116 sort size extsel hsbar vsbar
  edit "", 118, 3 153 109 10, tab 116 autohs
  button "Add", 119, 218 153 16 11, tab 116
  button "Rem", 120, 217 129 16 11, tab 116
  check "Hunt", 121, 113 153 17 11, tab 116 push
  edit "", 122, 46 141 29 10, tab 116
  text "Interval", 123, 26 143 20 8, tab 116
  button "WHOIS", 124, 217 45 22 11, tab 116
  button "Set", 125, 76 141 13 11, tab 116
  combo 129, 111 141 37 42, tab 116 drop
  text "OnNick", 130, 91 143 19 8, tab 116
  text "ChanFlagHistory", 133, 217 26 40 8, tab 116
  edit "", 134, 258 24 15 10, tab 116 limit 2
  button "Set", 135, 260 45 13 11, tab 116
  button "Show", 136, 220 91 18 11, tab 116
  button "Hide", 137, 220 103 18 11, tab 116
  button "Wipe", 138, 220 117 18 11, tab 116
  text "Data:", 139, 218 82 15 8, tab 116
  check "Status", 140, 4 141 20 11, tab 116 push
  check "StopWhenIRCOP", 141, 217 13 48 11, tab 116 push
  button "LISTall", 142, 217 69 22 11, tab 116
  edit "", 145, 180 153 22 10, tab 116
  button "Set", 144, 203 153 13 11, tab 116
  text "MinUsers", 143, 156 155 24 8, tab 116
  check "Regular", 146, 131 153 24 11, tab 116 push
  check "GlobalUpdates", 147, 149 141 40 11, tab 116 push
  button "LIST?", 148, 217 57 22 11, tab 116
  check "AllNicks", 149, 190 141 25 11, tab 116 push
  text "MaxNicksWhois", 150, 217 36 40 8, tab 116
  edit "", 151, 258 34 15 10, tab 116 limit 2
  tab "6. Bans", 131
  list 132, 3 13 245 107, tab 131 sort size extsel hsbar vsbar
  button "Rem", 152, 249 109 16 11, tab 131
}

alias cck.diaMAIN {
  var %d = CCK.MAIN | if (!$dialog(%d)) { dialog -mdv %d %d } | else { dialog -ev %d %d }
  did -f %d $gettok(1 2 3 4 116 131,$iif($1,$1,1),32)
}

; +++ DIALOG MAIN / LOAD CONFIG
; $1 = system or <ALL>
alias cck.diaMAIN.load {
  var %d = CCK.MAIN, %i | if (!$dialog(%d)) { return }
  if ($1 != <ALL>) { goto $1 }

  :Config
  var %h = cck.HConfig
  cck.diaMAIN.title
  didtok %d 21,19 32 ASK WARN KICK BAN
  didtok %d 88 32 ASK KICK BAN
  didtok %d 87 32 $cck.TrackUsers.Pars
  didtok %d 6 32 INW EXW INR EXR
  didtok %d 129 32 IGNORE LOG UPDATE ADD
  did -c %d 21 $didwm(%d,21,$hget(%h,ActBadChans))
  did -c %d 19 $didwm(%d,19,$hget(%h,ActBadStrings))
  did -c %d 88 $didwm(%d,88,$hget(%h,TrackUsers.DefaultAct))
  did $cck.dia.OOsw($hget(%h,CheckNicks.StopWhenIRCOP)) %d 141
  did $cck.dia.OOsw($hget(%h,TrackUsers.DefaultDo/List)) %d 108
  did $cck.dia.OOsw($hget(%h,UsePING)) %d 99
  did $cck.dia.OOsw($hget(%h,WinLog.ShowNonMatches)) %d 71
  did $cck.dia.OOsw($hget(%h,Exclude@)) %d 27
  did $cck.dia.OOsw($hget(%h,Exclude%)) %d 26
  did $cck.dia.OOsw($hget(%h,Exclude+)) %d 28
  did $cck.dia.OOsw($hget(%h,NoAbort@PART-KICK)) %d 33
  did $cck.dia.OOsw($hget(%h,DetailedWhois)) %d 70
  did $cck.dia.OOsw($hget(%h,MonitorQueue)) %d 96

  did -ra %d 22 $hget(%h,WarnMsg)
  did -ra %d 25 $hget(%h,KickMsg)
  did -ra %d 10 $hget(%h,TrackUsers.DefaultKickMsg)
  did -o %d 134 1 $hget(%h,CheckNicks.ChanFlagHistory)
  did -o %d 151 1 $hget(%h,CheckNicks.MaxNicksToWhois)
  did -o %d 72 1 $hget(%h,AskMsg.MaxDataLength)
  did -o %d 74 1 $hget(%h,WarnMsg.MaxDataLength)
  did -o %d 75 1 $hget(%h,KickMsg.MaxDataLength)
  did -o %d 80 1 $hget(%h,WinLog.MaxDataLength)
  did -o %d 60 1 $hget(%h,SweepChanInterval)
  did -o %d 126 1 $hget(%h,TimePollingInterval)
  did -o %d 114 1 $hget(%h,SweepNickInterval)
  did -o %d 61 1 $hget(%h,DeadGap)
  did -o %d 100 1 $hget(%h,NoPINGGap)
  did -o %d 103 1 $hget(%h,Timeout)
  did -o %d 62 1 $hget(%h,TimeFormat)
  did -o %d 31 1 $hget(%h,ExcludeNicks)
  did -o %d 55 1 $hget(%h,KeySweep)
  did -o %d 56 1 $hget(%h,KeyWARN)
  did -o %d 57 1 $hget(%h,KeyKICK)
  did -o %d 58 1 $hget(%h,KeyBAN)
  if ($1 != <ALL>) { return }

  :CheckChans
  did -r %d 11 | var %i = 1
  while ($hget(cck.HCheckChans,%i).item) { did -az %d 11 $cck.diaMAIN.D2L(CheckChans,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :CheckNicks
  did -r %d 117 | var %i = 1
  while ($hget(cck.HCheckNicks,%i).item) { did -az %d 117 $cck.diaMAIN.D2L(CheckNicks,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :BadChans
  did -r %d 34 | var %i = 1
  while ($hget(cck.HBadChans,%i).item) { did -az %d 34 $cck.diaMAIN.D2L(BadChans,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :BadStrings
  did -r %d 38 | var %i = 1
  while ($hget(cck.HBadStrings,%i).item) { did -az %d 38 $cck.diaMAIN.D2L(BadStrings,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :GoodChans
  did -r %d 42 | var %i = 1
  while ($hget(cck.HGoodChans,%i).item) { did -az %d 42 $cck.diaMAIN.D2L(GoodChans,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :TrackUsers
  did -r %d 5 | var %i = 1
  while ($hget(cck.HTrackUsers,%i).item) { did -az %d 5 $cck.diaMAIN.D2L(TrackUsers,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :Bans
  did -r %d 132 | var %i = 1, %f = $hget(cck.HDisk,Bans)
  while ($read(%f,%i)) { did -az %d 132 $cck.diaMAIN.D2L(Bans,$v1) | inc %i }
  if ($1 != <ALL>) { return }

  :Session
  did $cck.dia.OOsw($cck.queue.process) %d 98
  if ($1 != <ALL>) { return }

}

; +++ DIALOG MAIN / UPDATE AT CONFIG CHANGE
; $1 = Config $2 = par $3- = val
; $1 = CheckChans or CheckNicks or BadChans or BadStrings or GoodChans or TrackUsers $2 = ADD|REM|REP $3- = data reference parameters
; $1 = Session $2 = par $3- = val
; All dialog list lines should start with the reference parameters, comma's in them will be replaced with spaces before line search.
alias cck.diaMAIN.upd {
  var %d = CCK.MAIN | if (!$dialog(%d)) { return }
  var %par = $2, %val = $3-, %sw = $cck.dia.OOsw($3)
  goto $1

  :Config
  if (%par == VersionShort) { cck.diaMAIN.title }
  elseif (%par == ActBadChans) { did -c %d 21 $didwm(%d,21,%val) }
  elseif (%par == ActBadStrings) { did -c %d 19 $didwm(%d,19,%val) }
  elseif (%par == TrackUsers.DefaultAct) { did -c %d 88 $didwm(%d,88,%val) }
  elseif (%par == TrackUsers.DefaultDo/List) { did %sw %d 108 }
  elseif (%par == CheckNicks.StopWhenIRCOP) { did %sw %d 141 }
  elseif (%par == UsePING) { did %sw %d 99 }
  elseif (%par == WinLog.ShowNonMatches) { did %sw %d 71 }
  elseif (%par == Exclude@) { did %sw %d 27 }
  elseif (%par == Exclude%) { did %sw %d 26 }
  elseif (%par == Exclude+) { did %sw %d 28 }
  elseif (%par == NoAbort@PART-KICK) { did %sw %d 33 }
  elseif (%par == DetailedWhois) { did %sw %d 70 }
  elseif (%par == MonitorQueue) { did %sw %d 96 | cck.diaMAIN.title }
  elseif (%par == WarnMsg) { did -ra %d 22 %val }
  elseif (%par == KickMsg) { did -ra %d 25 %val }
  elseif (%par == TrackUsers.DefaultKickMsg) { did -ra %d 10 %val }
  elseif (%par == CheckNicks.ChanFlagHistory) { did -o %d 134 1 %val }
  elseif (%par == CheckNicks.MaxNicksToWhois) { did -o %d 151 1 %val }
  elseif (%par == AskMsg.MaxDataLength) { did -o %d 72 1 %val }
  elseif (%par == WarnMsg.MaxDataLength) { did -o %d 74 1 %val }
  elseif (%par == KickMsg.MaxDataLength) { did -o %d 75 1 %val }
  elseif (%par == WinLog.MaxDataLength) { did -o %d 80 1 %val }
  elseif (%par == SweepChanInterval) { did -o %d 60 1 %val }
  elseif (%par == SweepNickInterval) { did -o %d 114 1 %val }
  elseif (%par == TimePollingInterval) { did -o %d 126 1 %val }
  elseif (%par == DeadGap) { did -o %d 61 1 %val }
  elseif (%par == NoPINGGap) { did -o %d 100 1 %val }
  elseif (%par == Timeout) { did -o %d 103 1 %val }
  elseif (%par == TimeFormat) { did -o %d 62 1 %val }
  elseif (%par == ExcludeNicks) { did -o %d 31 1 %val }
  elseif (%par == KeySweep) { did -o %d 55 1 %val }
  elseif (%par == KeyWARN) { did -o %d 56 1 %val }
  elseif (%par == KeyKICK) { did -o %d 57 1 %val }
  elseif (%par == KeyBAN) { did -o %d 58 1 %val }
  return

  :CheckChans | var %id = 11 | goto upd
  :CheckNicks | var %id = 117 | goto upd
  :BadChans | var %id = 34 | goto upd
  :BadStrings | var %id = 38 | goto upd
  :GoodChans | var %id = 42 | goto upd
  :TrackUsers | var %id = 5 | goto upd
  :Bans | var %id = 132 | goto upd

  :upd
  if ($2 == ADD) { did -azk %d %id $cck.diaMAIN.D2L($1,$3-) }
  elseif ($2 == REP) {
    if ($didwm(%d,%id,$replace($3-,$chr(44),$chr(32)) *)) { 
      var %l = $v1 | did $cck.dia.RepSW(%d,%id,%l) %d %id %l $cck.diaMAIN.D2L($1,$3-) 
    }
  }
  elseif ($2 == REM) { did -dzk %d %id $didwm(%d,%id,$replace($3-,$chr(44),$chr(32)) *) }
  return

  :Session
  if (%par == QueueProcess) { did $cck.dia.OOsw(%val) %d 98 }

}

; Convert system data storage format to dialog list format
; Input: $1 = system $2- = reference parameters
; Output: $result = dialog list record
alias cck.diaMAIN.D2L {
  var %r = $2- | goto $1
  :CheckChans | tokenize 44 %r | return $1-2 $hget(cck.HCheckChans,%r)
  :CheckNicks | tokenize 44 %r | var %nn = $1-2 | tokenize 32 $hget(cck.HCheckNicks,%r) | return %nn $4 $7 $1 $5 $3 $8-11
  :BadChans | tokenize 44 %r | var %nc = $1-2 | tokenize 32 $hget(cck.HBadChans,%r) | return %nc $1 $iif($2 == Never,$2,$cck.time($2))
  :BadStrings | tokenize 32 $hget(cck.HBadStrings,%r) | return %r $1 $iif($2 == Never,$2,$cck.time($2))
  :GoodChans | tokenize 44 %r | var %nc = $1-2 | tokenize 32 $hget(cck.HGoodChans,%r) | return %nc $1 $iif($2 == Never,$2,$cck.time($2))
  :TrackUsers | tokenize 44 %r | return $1- $hget(cck.HTrackUsers,%r)
  :Bans | tokenize 32 $read($hget(cck.HDisk,Bans),w,& $2- *) | return $2- $iif($1 == PERM,$1,$cck.time($1))
}

; +++ DIALOG MAIN / UPDATE TITLE
alias cck.diaMAIN.title {
  if (!$dialog(CCK.MAIN)) { return }
  dialog -t CCK.MAIN $hget(cck.HConfig,VersionShort) Main Dialog $&
    $iif($hget(cck.HConfig,MonitorQueue) == ON,- Queue: $cck.queue.getstatus,$null)
}

; +++ DIALOG MAIN / EVENTS
on *:DIALOG:CCK.MAIN:init:0:{
  var %d = $dname
  did -b %d 17,16,106,107,29,36,37,68,40,41,43,44,59,7,89,110,111,8,90,85,82,119,120,124,142,148,125,135,136,137,138,144
  cck.diaMAIN.load <ALL>
  did -o %d 122 1 default
  did -o %d 145 1 15
  did -o %d 12,35,45 1 network channel
  did -o %d 118 1 network nick
  did -c %d 129 $didwm(%d,129,LOG)
  did -c %d 146,147,149
}
on *:DIALOG:CCK.MAIN:sclick:*:{
  var %d = $dname, %c = 0, %sw = $iif($did,$cck.dia.swOO(%d,$did))
  ; Config
  if ($did == 21) { cck.Config ActBadChans $$did(%d,21,0) }
  elseif ($did == 19) { cck.Config ActBadStrings $$did(%d,19,0) }
  elseif ($did == 99) { cck.Config UsePING %sw }
  elseif ($did == 71) { cck.Config WinLog.ShowNonMatches %sw }
  elseif ($did == 27) { cck.Config Exclude@ %sw }
  elseif ($did == 26) { cck.Config Exclude% %sw }  
  elseif ($did == 28) { cck.Config Exclude+ %sw }
  elseif ($did == 33) { cck.Config NoAbort@PART-KICK %sw }
  elseif ($did == 70) { cck.Config DetailedWhois %sw }
  elseif ($did == 96) { cck.Config MonitorQueue %sw }
  elseif ($did == 109) { cck.Config TrackUsers.DefaultDo/List $cck.dia.swOO(%d,108) }
  elseif ($did == 69) { cck.BadSound SET }
  elseif ($did == 29) {
    if ($did(%d,22).edited) { cck.Config WarnMsg $$didtok(%d,22,32) | if ($ok($result)) { inc %c | did -j %d 22 } }
    if ($did(%d,25).edited) { cck.Config KickMsg $$didtok(%d,25,32) | if ($ok($result)) { inc %c | did -j %d 25 } }
    if ($did(%d,72).edited) { cck.Config AskMsg.MaxDataLength $$did(%d,72) | if ($ok($result)) { inc %c | did -j %d 72 } }
    if ($did(%d,74).edited) { cck.Config WarnMsg.MaxDataLength $$did(%d,74) | if ($ok($result)) { inc %c | did -j %d 74 } }
    if ($did(%d,75).edited) { cck.Config KickMsg.MaxDataLength $$did(%d,75) | if ($ok($result)) { inc %c | did -j %d 75 } }
    if ($did(%d,80).edited) { cck.Config WinLog.MaxDataLength $$did(%d,80) | if ($ok($result)) { inc %c | did -j %d 80 } }
    if (%c) { did -b %d $did }
  }
  elseif ($did == 85) { cck.Config TrackUsers.DefaultAct $$did(%d,88,0) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 82) { cck.Config TrackUsers.DefaultKickMsg $$did(%d,10) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 68) {
    if ($did(%d,60).edited) { cck.Config SweepChanInterval $$did(%d,60) | if ($ok($result)) { inc %c | did -j %d 60 } }
    if ($did(%d,114).edited) { cck.Config SweepNickInterval $$did(%d,114) | if ($ok($result)) { inc %c | did -j %d 114 } }
    if ($did(%d,126).edited) { cck.Config TimePollingInterval $$did(%d,126) | if ($ok($result)) { inc %c | did -j %d 126 } }
    if ($did(%d,61).edited) { cck.Config DeadGap $$did(%d,61) | if ($ok($result)) { inc %c | did -j %d 61 } }
    if ($did(%d,100).edited) { cck.Config NoPINGGap $$did(%d,100) | if ($ok($result)) { inc %c | did -j %d 100 } }
    if ($did(%d,103).edited) { cck.Config Timeout $$did(%d,103) | if ($ok($result)) { inc %c | did -j %d 103 } }
    if ($did(%d,62).edited) { cck.Config TimeFormat $$did(%d,62) | if ($ok($result)) { inc %c | did -j %d 62 } }
    if ($did(%d,31).edited) { cck.Config ExcludeNicks $did(%d,31) | if ($ok($result)) { inc %c | did -j %d 31 } }      
    if (%c) { did -b %d $did }
  }
  elseif ($did == 59) {
    if ($did(%d,55).edited) { cck.Config KeySweep $$did(%d,55) | if ($ok($result)) { inc %c | did -j %d 55 } }
    if ($did(%d,56).edited) { cck.Config KeyWARN $$did(%d,56) | if ($ok($result)) { inc %c | did -j %d 56 } }
    if ($did(%d,57).edited) { cck.Config KeyKICK $$did(%d,57) | if ($ok($result)) { inc %c | did -j %d 57 } }
    if ($did(%d,58).edited) { cck.Config KeyBAN $$did(%d,58) | if ($ok($result)) { inc %c | did -j %d 58 } }
    if (%c) { did -b %d $did }
  }

  ; CheckChans
  elseif ($did == 11) {
    if ($did(%d,11,0).sel == 1) {
      tokenize 32 $did(%d,11,$did(%d,11,1).sel)
      did -o %d 12 1 $1-2
      did $cck.dia.OOsw($3) %d 13
      did $cck.dia.OOsw($4) %d 14
      did $cck.dia.OOsw($5) %d 15
      did $cck.dia.OOsw($6) %d 81
    }
    else { did -u %d 13,14,15,81 }
    did -e %d 16,106,107 | did -b %d 17
  }
  elseif ($did == 17) {
    cck.CheckChans.Add $$did(%d,12) $cck.dia.swOO(%d,13,14,15,81)
    if ($ok($result)) { did -b %d $did }
  }
  elseif ($did == 16) {
    var %i = 1, %h = $rhash | while ($did(%d,11,%i).sel) { tokenize 32 $did(%d,11,$v1) | hadd %h %i $1-2 | inc %i }
    var %i = 1 | while ($hget(%h,%i)) { cck.CheckChans.Rem $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d 16,106,107 }
  }
  elseif ($istok(106 107,$did,32)) {
    var %i = 1, %c = $iif($did == 106,JOIN,PART)
    while ($did(%d,11,%i).sel) { tokenize 32 $did(%d,11,$v1) | cck.queue.add $1 %c $2 - - CHECKCHANS-UIL | inc %i }
  }
  elseif ($istok(13 14 15 81,$did,32)) {
    var %p = $gettok(USERJOIN OWNJOIN TIMER CONTINUE,$findtok(13 14 15 81,$did,1,32),32), %i = 1
    while ($did(%d,11,%i).sel) {
      tokenize 32 $did(%d,11,$v1) | cck.CheckChans.Change $1-2 %p $cck.dia.swOO(%d,$did) | inc %i
    }
  }

  ; CheckNicks
  elseif ($did == 117) {
    if ($did(%d,117,0).sel == 1) {
      tokenize 32 $did(%d,117,$did(%d,117,1).sel)
      did -o %d 118 1 $1-2
      did -o %d 122 1 $5
      did -o %d 145 1 $8
      did $+(-,$iif($7 == HUNT,-c,-u)) %d 121
      did $+(-,$iif($4 == ON,-c,-u)) %d 140
      did $+(-,$iif($9 == ON,-c,-u)) %d 146
      did $+(-,$iif($10 == ON,-c,-u)) %d 147
      did $+(-,$iif($11 == ON,-c,-u)) %d 149
      did -c %d 129 $didwm(%d,129,$6)
      var %w = @CCK. $+ $s2c($1,$2) | if ($window(%w)) { window -a %w | dialog -v %d }
    }
    else { did -u %d 121,140,146,147,149 }
    did -e %d 120,124,136,137,138,142,148 | did -b %d 119
  }
  elseif ($did == 119) {
    cck.CheckNicks.Add $$did(%d,118) $$did(%d,122) $iif($did(%d,121).state,HUNT,NOHUNT) $$did(%d,129,0) LUI $cck.dia.swOO(%d,140) $$did(%d,145) $&
      $cck.dia.swOO(%d,146) $cck.dia.swOO(%d,147) $cck.dia.swOO(%d,149)
    if ($ok($result)) { did -b %d $did }
  }
  elseif ($did == 120) {
    var %i = 1, %h = $rhash | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | hadd %h %i $1-2 | inc %i }
    var %i = 1 | while ($hget(%h,%i)) { cck.CheckNicks.Rem $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d 120,124,142,148,136,137,138 }
  }
  elseif ($did == 121) {
    var %i = 1
    while ($did(%d,117,%i).sel) {
      tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 HUNT $iif($did(%d,121).state,HUNT,NOHUNT) | inc %i
    }
  }
  elseif ($did == 125) {
    var %i = 1
    while ($did(%d,117,%i).sel) {
      tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 INTERVAL $did(%d,122) | inc %i
    }
    did -b %d $did
  }
  elseif ($did == 124) {
    var %i = 1
    while ($did(%d,117,%i).sel) {
      tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.SweepNicks $1-2 $+(CHECKNICKS-UIL,;?;?;,$2)
      inc %i
    }
  }
  elseif ($did == 144) {
    var %i = 1
    while ($did(%d,117,%i).sel) {
      tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 MINUSERS $did(%d,145) | inc %i
    }
    did -b %d $did
  }
  elseif ($did == 142) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.ListChans $1-2 UIL ALL | inc %i } }
  elseif ($did == 148) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.ListChans $1-2 UIL MISSING | inc %i } }
  elseif ($did == 140) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 STATUS $cck.dia.swOO(%d,$did) | inc %i } }
  elseif ($did == 147) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 GLOBALUPDATES $cck.dia.swOO(%d,$did) | inc %i } }
  elseif ($did == 149) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 ALLNICKS $cck.dia.swOO(%d,$did) | inc %i } }
  elseif ($did == 146) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 ONLYREGULAR $cck.dia.swOO(%d,$did) | inc %i } }
  elseif ($did == 136) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.ShowData $1-2 LUI | inc %i } | dialog -v %d }
  elseif ($did == 137) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | window -c $+(@CCK.,$1,$chr(44),$2) | inc %i } }
  elseif ($did == 138) { var %i = 1 | while ($did(%d,117,%i).sel) { tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.WipeData $1-2 | inc %i } }
  elseif ($did == 129) {
    if ($did(%d,117,1).sel) {
      var %i = 1
      while ($did(%d,117,%i).sel) {
        tokenize 32 $did(%d,117,$v1) | cck.CheckNicks.Change $1-2 NICKCHANGE $did(%d,129,0) | inc %i
      }
    }
    else {
      if ($numtok($did(%d,118),32) == 2) && ($cck.dia.CheckEdit(%d,122,145)) && ($cck.dia.CheckCombo(%d,129)) { did -e %d 119 } | else { did -b %d 119 }
    }
  }
  elseif ($did == 135) {
    if ($did(%d,134).edited) { cck.Config CheckNicks.ChanFlagHistory $$did(%d,134) | if ($ok($result)) { inc %c | did -j %d 134 } }
    if ($did(%d,151).edited) { cck.Config CheckNicks.MaxNicksToWhois $$did(%d,151) | if ($ok($result)) { inc %c | did -j %d 151 } }
    if (%c) { did -b %d $did }
  }
  elseif ($did == 141) { cck.Config CheckNicks.StopWhenIRCOP %sw }

  ; BadChans
  elseif ($did == 34) {
    if ($did(%d,34,0).sel == 1) { tokenize 32 $did(%d,34,$did(%d,34,1).sel) | did -o %d 35 1 $1-2 }
    did -e %d 37 | did -b %d 36
  }
  elseif ($did == 36) { cck.BadChans.Add $$did(%d,35) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 37) {
    var %i = 1, %h = $rhash | while ($did(%d,34,%i).sel) { tokenize 32 $did(%d,34,$v1) | hadd %h %i $1-2 | inc %i }
    var %i = 1 | while ($hget(%h,%i)) { cck.BadChans.Rem $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d $did }
  }

  ; BadStrings
  elseif ($did == 38) {
    if ($did(%d,38,0).sel == 1) { tokenize 32 $did(%d,38,$did(%d,38,1).sel) | did -o %d 39 1 $1 }
    did -e %d 41 | did -b %d 40
  }
  elseif ($did == 40) { cck.BadStrings.Add $$did(%d,39) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 41) {
    var %i = 1, %h = $rhash | while ($did(%d,38,%i).sel) { tokenize 32 $did(%d,38,$v1) | hadd %h %i $1 | inc %i }
    var %i = 1 | while ($hget(%h,%i)) { cck.BadStrings.Rem $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d $did }
  }

  ; GoodChannels
  elseif ($did == 42) {
    if ($did(%d,42,0).sel == 1) {
      tokenize 32 $did(%d,42,$did(%d,42,1).sel)
      did -o %d 45 1 $1-2
    }
    did -e %d 44 | did -b %d 43
  }
  elseif ($did == 43) { cck.GoodChans.Add $$did(%d,45) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 44) {
    var %i = 1, %h = $rhash | while ($did(%d,42,%i).sel) { tokenize 32 $did(%d,42,$v1) | hadd %h %i $1-2 | inc %i }
    var %i = 1 | while ($hget(%h,%i)) { cck.GoodChans.Rem $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d $did }
  }

  ; TrackUsers
  elseif ($did == 5) {
    if ($did(%d,5,0).sel == 1) {
      tokenize 32 $did(%d,5,$did(%d,5,1).sel)
      did -o %d 92 1 $1 | %c = $left($2,2) $+ $right($2,1)
      if ($istok(INW EXW INR EXR,%c,32)) {
        did -c %d 6 $didwm(%d,6,%c)
        did -c %d 87 $didwm(%d,87,$3)
        did -o %d 83 1 $4-
      }
      elseif ($2 == ACT) { did -c %d 88 $didwm(%d,88,$3) | did -e %d 85 }
      elseif ($2 == KICKMSG) { did -o %d 10 1 $3- | did -e %d 82 }
      elseif ($2 == DO/LIST) { did -c %d 108 }
      did -e %d 110
    }  
    did -e %d 90,8 | did -b %d 7,89
  }
  elseif ($did == 90) { cck.dia.ListSelTokSame %d 5 1 32 }
  elseif ($istok(6 87,$did,32)) {
    if ($cck.dia.CheckCombo(%d,6,87)) && ($cck.dia.CheckEdit(%d,92,83)) { did -e %d 7 } | else { did -b %d 7 }
    did -u %d 5 | did -b %d 90,8
  }
  elseif ($did == 88) {
    if ($cck.dia.CheckCombo(%d,88)) {
      if ($cck.dia.CheckEdit(%d,92)) { did -e %d 111 } | else { did -b %d 111 }
      did -e %d 85
    }
  }
  elseif ($did == 87) {
    if ($cck.dia.CheckCombo(%d,88)) {
      if ($cck.dia.CheckEdit(%d,92)) { did -e %d 111 } | else { did -b %d 111 }
      did -e %d 11
    }
  }
  elseif ($did == 8) {
    var %i = 1, %h = $rhash
    while ($did(%d,5,%i).sel) {
      tokenize 32 $did(%d,5,$v1)
      if ($istok(ACT KICKMSG DO/LIST,$2,32)) { hadd %h %i cck.TrackUsers.RemConfig $1-2 }
      elseif ($istok(INW EXW INR EXR,$left($2,2) $+ $right($2,1),32)) { hadd %h %i cck.TrackUsers.RemRule $1-3 }
      inc %i
    }
    var %i = 1 | while ($hget(%h,%i)) { $v1 | if ($ok($result)) { inc %c } | inc %i }
    dec %i | hfree %h | if (%c == %i) { did -b %d $did }
  }
  elseif ($did == 7) {
    cck.TrackUsers.AddRule $$did(%d,92) $$did(%d,6,0) $$did(%d,87,0) $$did(%d,83)
    if ($ok($result)) { did -b %d $did }
  }
  elseif ($did == 111) {
    cck.TrackUsers.AddConfig $$did(%d,92) ACT $$did(%d,88,0) | if ($ok($result)) { inc %c }
    if (%c) { did -b %d $did }
  }
  elseif ($did == 89) {
    cck.TrackUsers.AddConfig $$did(%d,92) KICKMSG $$did(%d,10) | if ($ok($result)) { inc %c }
    if (%c) { did -b %d $did }
  }
  elseif ($did == 110) {
    cck.TrackUsers.AddConfig $$did(%d,92) DO/LIST n/a | if ($ok($result)) { inc %c }
    if (%c) { did -b %d $did }
  }

  ; Bans

  ; Session
  elseif ($did == 97) { cck.queue.remnet * }
  elseif ($did == 112) { cck.queue.recover }
  elseif ($did == 98) { cck.queue.process %sw }

}
on *:DIALOG:CCK.MAIN:edit:*:{
  var %d = $dname
  ; Config
  if ($istok(22 25 72 74 75 80,$did,32)) { if ($cck.dia.CheckEdit(%d,22,25,72,74,75,80)) { did -e %d 29 } | else { did -b %d 29 } }
  elseif ($istok(60 114 126 61 62 31 100 103,$did,32)) { if ($cck.dia.CheckEdit(%d,60,114,126,61,62,100,103)) { did -e %d 68 } | else { did -b %d 68 } }
  elseif ($istok(55 56 57 58,$did,32)) { if ($cck.dia.CheckEdit(%d,55,56,57,58)) { did -e %d 59 } | else { did -b %d 59 } }
  ; CheckChans
  elseif ($did == 12) { did -u %d 11 | did -b %d 16 | if ($numtok($did(%d,$did),32) == 2) { did -e %d 17 } | else { did -b %d 17 } }
  ; CheckNicks
  elseif ($did == 118) {
    did -u %d 117 | did -b %d 120,124,136,137,138,142,148
    if ($numtok($did(%d,$did),32) == 2) && ($cck.dia.CheckEdit(%d,122,145)) && ($cck.dia.CheckCombo(%d,129)) { did -e %d 119 } | else { did -b %d 119 }
  }
  elseif ($did == 122) {
    if ($did(%d,117,1).sel) { if ($cck.dia.CheckEdit(%d,122)) { did -e %d 125 } | else { did -b %d 125 } }
    else {
      if ($numtok($did(%d,118),32) == 2) && ($cck.dia.CheckEdit(%d,122,145)) && ($cck.dia.CheckCombo(%d,129)) { did -e %d 119 } | else { did -b %d 119 }
    }
  }
  elseif ($did == 145) {
    if ($did(%d,117,1).sel) { if ($cck.dia.CheckEdit(%d,145)) { did -e %d 144 } | else { did -b %d 144 } }
    else {
      if ($numtok($did(%d,118),32) == 2) && ($cck.dia.CheckEdit(%d,122,145)) && ($cck.dia.CheckCombo(%d,129)) { did -e %d 119 } | else { did -b %d 119 }
    }
  }
  elseif ($istok(134 151,$did,32)) {
    if ($cck.dia.CheckEdit(%d,134,151)) { did -e %d 135 } | else { did -b %d 135 }
  }
  ; BadChans
  elseif ($did == 35) { did -u %d 34 | did -b %d 37 | if ($numtok($did(%d,$did),32) == 2) { did -e %d 36 } | else { did -b %d 36 } }
  ; BadStrings
  elseif ($did == 39) { did -u %d 38 | did -b %d 41 | if ($numtok($did(%d,$did),32) == 1) { did -e %d 40 } | else { did -b %d 40 } } 
  ; GoodChans
  elseif ($did == 45) { did -u %d 42 | did -b %d 44 | if ($numtok($did(%d,$did),32) == 2) { did -e %d 43 } | else { did -b %d 43 } }
  ; TrackUsers
  elseif ($istok(92 83,$did,32)) {
    did -u %d 5 | did -b %d 90,8
    if ($cck.dia.CheckCombo(%d,6,87)) && ($cck.dia.CheckEdit(%d,92,83)) { did -e %d 7 } | else { did -b %d 7 }
    if ($cck.dia.CheckEdit(%d,92)) && ($cck.dia.CheckCombo(%d,88)) { did -e %d 82 } | else { did -b %d 82 }
  }
  elseif ($did == 10) {
    did -u %d 5 | did -b %d 90,8,7,111,110
    if ($cck.dia.CheckEdit(%d,92,10)) { did -e %d 82,89 } | else { did -b %d 82,89 }
  }
}

;===================================================================================================
; DIALOG ASK
;===================================================================================================

; +++ DIALOG ASK / TABLE
; <timestamp> <# or $ or T> <network> <nick> <bad channels or tag of first TrackUsers match>
dialog CCK.ASK {
  title ""
  size -1 -1 274 102
  option dbu
  list 1, 0 1 274 90, size extsel hsbar vsbar
  button "Warn", 2, 1 92 18 10
  button "Kick", 3, 20 92 15 10
  button "KickBan", 4, 36 92 24 10
  button "Sweep", 5, 173 92 21 10, flat
  button "", 6, 175 94 8 7, hide ok
  button "Rem", 7, 258 92 15 10
  check "Empty->Close", 8, 61 92 38 10, push
  button "All", 9, 234 92 11 10
  edit "", 10, 143 92 15 10, limit 3
  text "MaxDataLength", 11, 103 94 39 8
  button "Set", 12, 159 92 13 10
  button "Sel T", 13, 195 92 17 10
  button "#", 14, 214 92 9 10
  button "$", 15, 224 92 9 10
  button "CB", 16, 246 92 11 10
}
alias cck.diaASK { var %d = CCK.ASK | if (!$dialog(%d)) { dialog -mdv %d %d } | else { dialog -ev %d %d } }

; +++ DIALOG ASK / LOAD CONFIG
alias cck.diaASK.load {
  var %d = CCK.ASK | if (!$dialog(%d)) { return }
  cck.diaASK.title
  did $cck.dia.OOsw($hget(cck.HConfig,DiaASK.CloseWhenEmpty)) %d 8
  did -o %d 10 1 $hget(cck.HConfig,DiaASK.MaxDataLength)
}

; +++ DIALOG ASK / UPDATE AT CONFIG CHANGE
; <par> <val>
alias cck.diaASK.upd {
  var %d = CCK.ASK | if (!$dialog(%d)) { return }
  var %par = $1, %val = $2-
  if (%par == VersionShort) { cck.diaASK.title }
  elseif (%par == DiaASK.CloseWhenEmpty) { did $cck.dia.OOsw($2) %d 8 }
  elseif (%par == DiaASK.MaxDataLength) { did -o %d 10 1 %val }
}

; +++ DIALOG ASK / UPDATE TITLE
alias cck.diaASK.title {
  if (!$dialog(CCK.ASK)) { return }
  dialog -t CCK.ASK $hget(cck.HConfig,VersionShort) Ask Dialog $&
    $iif($hget(cck.HConfig,MonitorQueue) == ON,- Queue: $cck.queue.getstatus,$null)
}

; +++ DIALOG ASK / EVENTS
on *:DIALOG:CCK.ASK:init:0:{ did -b $dname 2,3,4,7,12,16 | cck.diaASK.load }
on *:DIALOG:CCK.ASK:sclick:*:{
  var %d = $dname, %c = 0
  if ($did == 1) { did -e %d 2,3,4,7,16 }
  elseif ($istok(2 3 4,$did,32)) {
    var %act = $gettok(WARN KICK BAN,$findtok(2 3 4,$did,1,32),32), %t = $did(%d,1,0).sel, %i = 1
    while (%i <= %t) {
      tokenize 32 $did(%d,1,$did(%d,1,%i).sel)
      var %actmsg = $iif($2 == T,$6-,$cck.actmsg(%act,$3,$4,$6-))
      cck.act %act $3 $4 $5 %actmsg
      inc %i
    }
  }
  elseif ($did == 5) { cck.CheckChans.Sweep UIL }
  elseif ($did == 7) { while ($did(%d,1,1).sel) { did -dzk %d 1 $v1 } | did -b %d 2,3,4,7,16 }
  elseif ($did == 8) { cck.Config DiaASK.CloseWhenEmpty $cck.dia.swOO(%d,8) }
  elseif ($did == 9) { did -c %d 1 | if ($did(%d,1,1).sel) { did -e %d 7,2,3,4,16 } | else { did -b %d 7,2,3,4,16 } }
  elseif ($istok(13 14 15,$did,32)) {
    %c = $gettok(T $chr(35) $,$findtok(13 14 15,$did,1,32),32)
    did -u %d 1 | cck.dia.ListSelTokVal %d 1 2 32 %c
    if ($did(%d,1,1).sel) { did -e %d 7,2,3,4,16 } | else { did -b %d 7,2,3,4,16 }
  }
  elseif ($did == 12) { cck.Config DiaASK.MaxDataLength $$did(%d,10) | if ($ok($result)) { did -b %d $did } }
  elseif ($did == 16) { var %i = 1 | while ($did(%d,1,%i).sel) { clipboard -an $did(%d,1,$v1) | inc %i } }
}
on *:DIALOG:CCK.ASK:edit:*:{
  var %d = $dname
  if ($did == 10) { did $iif($did(%d,10) != $null,-e,-b) %d 12 }
}

; DIALOG ASK / UPDATE LIST
; List layout: timestamp #|$|T network nick user@host badchans|trackuserskickmsg
; If nick already present, replace according to priority 1) TrackUsers T 2) BadChans # 3) BadStrings $
; <# or $ or T> <network> <nick> <user@host> <foundchans or TrackUser first matching tag>
alias cck.diaASK.add {
  var %d = CCK.ASK | if (!$dialog(%d)) { cck.DiaASK }
  var %l = $didwm(%d,1,& & $2 $3 *) | if (!%l) { did -azk %d 1 $cck.time $1- | return }
  var %rec = $did(%d,1,%l), %type = $gettok(%rec,2,32), %p1 = $pos(T#$,$1,1), %p2 = $pos(T#$,%type,1)
  if (%p1 <= %p2) { did -ok %d 1 %l $cck.time $1- }
}
; <network> <nick> (wildcards allowed)
alias cck.DiaASK.rem {
  var %d = CCK.ASK | if (!$dialog(%d)) { return }
  if ($didwm(%d,1,& & $1 $2 *)) { did -dzk %d 1 $v1 | cck.DiaASK.closeempty | return }
}
; <network> <nick>
alias cck.DiaASK.get {
  if ($dialog(CCK.ASK)) && ($didwm(CCK.ASK,1,& & $1 $2 *)) { return $did(CCK.ASK,1,$v1) }
  return 0
}
; <network>
alias cck.DiaASK.getlatest { 
  var %d = CCK.ASK | if (!$dialog(%d)) { return 0 }
  var %i = $did(%d,1).lines
  while (%i) { var %rec = $did(%d,1,%i) | if ($gettok(%rec,3,32) == $1) { return %rec } | dec %i }
  return 0
}
; <network> <nick> <newnick>
alias cck.DiaASK.updnick {
  var %d = CCK.ASK
  if ($dialog(%d)) && ($didwm(%d,1,& & $1 $2 *)) {
    var %l = $v1 | did -ok %d 1 %l $puttok($did(%d,1,%l),$3,4,32) | return 1
  }
  return 0
}
; <network> <channel> (to exclude - pass through to onchan function)
alias cck.DiaASK.clean {
  var %d = CCK.ASK
  if (!$dialog(%d)) { return }
  var %net = $1, %chan = $2, %t = $did(%d,1).lines, %i = 1
  while (%i <= %t) {
    tokenize 32 $did(%d,1,%i)
    if ($3 == %net) && (!$cck.onchan(*,$3,$4,%chan)) { did -dzk %d 1 %i | dec %t } | else { inc %i }
  }
  cck.DiaASK.closeempty
}
; assumed present
alias cck.DiaASK.closeempty {
  if ($hget(cck.HConfig,DiaASK.CloseWhenEmpty) == ON) && (!$did(CCK.ASK,1).lines) { dialog -k CCK.ASK }
  elseif (!$did(CCK.ASK,1,0).sel) { did -b CCK.ASK 7,2,3,4,16 }
}

;===================================================================================================
; IRC / VARIOUS
;===================================================================================================

on *:TEXT:!whois &:#:{
  if (!$cck.CheckChans.Check($network,$chan,ANY)) { return }
  cck.queue.add $network WHOIS $$2 - $iif($address($2,0),$gettok($v1,2-,33),-) $+(CHECKCHANS-UIR;,$chan)
}
on *:TEXT:!whoislist &:#:{
  if (!$cck.CheckChans.Check($network,$chan,ANY)) { return }
  cck.queue.add $network WHOIS $$2 - $iif($address($2,0),$gettok($v1,2-,33),-) $+(CHECKCHANS-UIR;,$chan,;LIST)
}
on *:NICK: {
  cck.CheckNicks.UpdateNick $network $nick $newnick
  cck.queue.updnick $network $nick $newnick
  cck.diaASK.updnick $network $nick $newnick
  cck.CheckNicks.AddDataGlobal $network HOST $site NICK $nick
  cck.CheckNicks.AddDataGlobal $network HOST $site NICK $newnick
}
on *:QUIT: {
  cck.queue.rem $network WHOIS $nick - *
  cck.queue.rem $network KICK & - $nick
  cck.queue.rem $network MODE & & $nick
  cck.queue.rem $network MSG $nick - -
  cck.queue.rem $network NOTICE $nick - -
  cck.queue.rem $network DESCRIBE $nick - -
  cck.diaASK.rem $network $nick
  cck.CheckNicks.AddDataGlobal $network HOST $site NICK $nick
}
on *:DISCONNECT: { hdel -w cck.H $+($network,$chr(44),*) | cck.queue.remnet $network | cck.diaASK.rem $network * }

;===================================================================================================
; IRC / WHOIS
;===================================================================================================

; +++ IRC EVENTS / WHOIS
#cck.raw.whois on
raw 275:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw SSL 1 } }
raw 301:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw AWAY $3- } }
raw 307:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw REGNICK $2 } }
raw 310:*help*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw HELPOP 1 } }
raw 310:*is using modes*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw MODES $6- } }
raw 311:*:{
  if ($cck.raw(WHOIS,$2,-,*)) { 
    var %c = hadd cck.HRaw | %c NETWORK $network | %c NICK $2 | %c USER $3 | %c HOST $4 | %c REALNAME $6-
    %c MASK $+($2,!,$3,@,$4)  
  }
}
raw 312:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw SERVHOST $3 | hadd cck.HRaw SERVINFO $4- } }
raw 313:*:{
  if (!$cck.raw(WHOIS,$2,-,*)) { return }
  if (service isin $3-) { hadd cck.HRaw SERVICE 1 }
  if (operator isin $3-) || (admin isin $3-) { hadd cck.HRaw IRCOP 1 }
}
raw 317:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw ^IDLE $3 | hadd cck.HRaw ^LOGON $4 } }
raw 318:*:{ if ($cck.raw(WHOIS,$2,-,*)) { cck.whois.process $2 } }
raw 319:*:{
  if (!$cck.raw(WHOIS,$2,-,*)) { return }
  var %i = 3 | while ($ [ $+ [ %i ] ] != $null) { aline @cck.raw $v1 | inc %i }
}
raw 330:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw AUTHNAME $3 } }
raw 335:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw BOT 1 } }
raw 338:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw REALHOST $3 } }
raw 378:*:{
  if (!$cck.raw(WHOIS,$2,-,*)) { return }
  hadd cck.HRaw REALHOST $iif(@ isin $6,$gettok($6,2,64),$6)
  hadd cck.HRaw USERIP $7
}
raw 379:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw MODES $6- } }
raw 401:*:{
  if ($cck.raw(WHOIS,$2,-,*)) {
    hadd cck.HRaw OFFLINE 1
    if ($network == SwiftIRC) { cck.whois.process $2 }
  }
}
raw 402:*:{
  if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw OFFLINE 1 | cck.whois.process $2 }
}
raw 535:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw FILTERING 1 } }
raw 537:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw IMMUNETOFILTERING 1 } }
raw 671:*secure*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw SSL 1 } }
raw 671:*ssl*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw SSL 1 } }
raw 672:*:{ if ($cck.raw(WHOIS,$2,-,*)) { hadd cck.HRaw CGI:IRC 1 } }
raw 716:*:{
  if (!$cck.raw(WHOIS,$2,-,*)) { return }
  if ($5 === +G) { hadd cck.HRaw SOFTCALLERID 1 }
  elseif ($5 === +g) { hadd cck.HRaw CALLERID 1 }
}
#cck.raw.whois end

; IN32 $1 = nick // Data in hash cck.Hraw + win @cck.raw
alias cck.whois.process {
  var %nick = $1, %hc = cck.HConfig, %w = @cck.raw, %h = cck.HRaw
  var %s = $cck.queue.get($network,WHOIS,%nick,-,*) | if (!%s) { cck.rawclean | return }
  tokenize 32 %s | var %pr = $1, %addr = $6, %sysdata = $7, %sys = $gettok($7,1,59), %sysd = $gettok($7,2-,59)
  if ($istok(%sys,CHECKNICKS,45)) { cck.CheckNicks.Change $network %nick CTIME $ctime }
  if ($hget(%h,OFFLINE)) {
    if ($istok(%sys,CHECKNICKS,45)) { cck.CheckNicks.HuntOffline $network %nick %sysdata }
    if (%addr == -) { cck.rawclean | return }
    tokenize 64 %addr | hadd %h USER $1 | hadd %h HOST $2 | hadd %h MASK $+(%nick,!,%addr)
  }  
  if ($istok(%sys,CHECKNICKS,45)) { cck.CheckNicks.AddData $network %nick %sysdata %h %w | cck.rawclean | return }
  %addr = $+($hget(%h,USER),@,$hget(%h,HOST))
  var %dolist = $iif($istok(%sysd,LIST,59),1,0), %wr = $rwin, %show | filter -ww %w %wr *
  if ($hget(%hc,WinLog.ShowNonMatches) == ON) {
    if (!$istok(%sys,CONTINUE,45)) && (!$istok(%sys,TIMER,45)) { var %show = 1 }
  }
  :track
  var %track = $cck.TrackUsers.Check(%h,%wr)
  if (%show) || (%track) {
    tokenize 32 %track
    var %c = cck.echoLog $+(,$iif(%track,04,10),$network %nick,)
    if (%track) { %c matches TrackUsers RuleSet "12 $+ $2 $+ " - Action12 $1 $+ : }
    if ($hget(%h,OFFLINE)) { %c OFFLINE }
    %c USER: $hget(%h,USER) HOST: $hget(%h,HOST)
    %c REALNAME: $hget(%h,REALNAME)
    var %s
    if ($hget(%h,REGNICK) != $null) { %s = REGNICK: $v1 }
    if ($hget(%h,AUTHNAME) != $null) { %s = %s AUTHNAME: $v1 }
    if ($hget(%h,USERIP) != $null) { %s = %s USERIP: $v1 }
    if ($hget(%h,REALHOST) != $null) { %s = %s REALHOST: $v1 }
    if (%s) { %c %s }
    if ($hget(%h,AWAY) != $null) { %c AWAY: $v1 }
    if ($hget(%h,MODES) != $null) { %c AWAY: $v1 }
    var %s
    if ($hget(%h,^LOGON) != $null) { %s = ^LOGON: $asctime($v1,$hget(cck.HConfig,TimeFormat)) }
    if ($hget(%h,^IDLE) != $null) { %s = %s ^IDLE: $duration($v1) }
    if (%s) { %c %s }
    %c SERVHOST: $hget(%h,SERVHOST) SERVINFO: $hget(%h,SERVINFO)
    var %s
    if ($hget(%h,IRCOP) != $null) { var %s = FLAGS: IRCOP }
    if ($hget(%h,HELPOP) != $null) { var %s = %s HELPOP }
    if ($hget(%h,SERVICE) != $null) { var %s = %s SERVICE }
    if ($hget(%h,BOT) != $null) { var %s = %s BOT }
    if ($hget(%h,SSL) != $null) { var %s = %s SSL } 
    if ($hget(%h,FILTERING) != $null) { var %s = %s FILTERING }
    if ($hget(%h,IMMUNETOFILTERING) != $null) { var %s = %s IMMUNETOFILTERING }
    if ($hget(%h,CALLERID) != $null) { var %s = %s CALLERID }
    if ($hget(%h,SOFTCALLERID) != $null) { var %s = %s SOFTCALLERID }
    if ($hget(%h,CGI:IRC) != $null) { var %s = %s CGI:IRC } 

    if (%s) { %c %s }
  }
  if (!%track) { window -c %wr | %s = $+(10,$network %nick,) | goto channels }
  %s = $+(04,$network %nick,)
  if ($istok(KICK BAN,$1,32)) {
    cck.act $1 $network %nick %addr $cck.limittokenstring(32,$hget(%hc,KickMsg.MaxDataLength),$4-)
    var %actdone = 1
  }
  elseif ($1 == ASK) {
    cck.mychans $network %nick echo -t 10 $+ $network %nick %addr is a TrackUsers RuleSet12 $2 match $+ . Press $cck.key.info
    if ($cck.onchan(*,$network,%nick)) {
      cck.diaASK.add T $network %nick %addr $cck.limittokenstring(32,$hget(%hc,DiaASK.MaxDataLength),$4-)
      cck.badsound
    }
  }
  ;TODO check if system name replacement causes problems
  if ($3 == DO/LIST) { %sys = TRACKUSERS | %dolist = 1 }
  window -c %wr

  :channels
  if ($hget(%h,OFFLINE)) { cck.echoLog %s OFFLINE | cck.rawclean | return }
  if (!$line(%w,1)) { cck.echoLog %s not found on any visible channels. | cck.rawclean | return }
  var %i = 0, %foundbad = 0, %foundstring = 0, %list, %list1, %p, %c, %pchanc, %pchan, %len
  :channels.next
  inc %i | %pchan = $line(%w,%i) | if (%pchan == $null) { goto channels.act }
  %pchanc = $cck.getprefixchan($network,%pchan)
  if (%pchanc == INVALID) { 
    %list1 = %list %pchan $+ (04ParsingFailure)
    rline %w %i %pchanc NONE
    cck.echo- SYSTEM: Parsing failure ( $+ $network $+ ) for channel12 %pchan $+ .
    goto channels.output
  }
  tokenize 32 %pchanc | %p = $1 | %c = $2
  if (%dolist) { cck.queue.add $network LIST %c - - $+(%sys,;,%nick,;,%addr,;,%p) }
  if ($cck.BadChans.Check($network,%c)) {
    rline %w %i %c $hget(%hc,ActBadChans) | inc %foundbad | %list1 = %list $+(%p,04,%c,)
  }
  elseif ($cck.BadStrings.Check(%c)) {
    if ($cck.GoodChans.Check($network,%c)) { dline %w %i | dec %i | %list1 = %list $+(%p,03,%c,) }
    else { rline %w %i %c $hget(%hc,ActBadStrings) | inc %foundstring | %list1 = %list $+(%p,07,%c,) }
  }
  else { dline %w %i | dec %i | %list1 = %list %p $+ %c }
  :channels.output
  %len = $len(%list1)
  if (%len < $hget(%hc,WinLog.MaxDataLength)) { %list = %list1 | goto channels.next }
  if (%show) || ($calc(%foundbad + %foundstring)) || (%track) { cck.echoLog %s CHANNELS: %list }
  if (%len > $hget(%hc,WinLog.MaxDataLength)) { %list = $gettok(%list1,-1,32), %list1 } | else { %list = $null }
  goto channels.next
  :channels.act
  if (%list) && ((%show) || ($calc(%foundbad + %foundstring))) || (%track) { cck.echoLog %s is on %list }
  if (!$calc(%foundbad + %foundstring)) { cck.rawclean | return }
  var %ask = $fline(%w,& ASK,1), %warn = $fline(%w,& WARN,1), %kick = $fline(%w,& KICK,1), %ban = $fline(%w,& BAN,1)
  if ($fline(%w,& BAN,1)) {
    if (!%actdone) {
      filter -wwc %w %w & BAN
      var %msg = $cck.col2tok(%w,32,1,32,$hget(%hc,KickMsg.MaxDataLength),</cck>)
      cck.act BAN $network %nick %addr $cck.actmsg(BAN,$network,%nick,%msg)
    }
  }
  elseif ($fline(%w,& KICK,1)) {
    if (!%actdone) {
      filter -wwc %w %w & KICK
      var %msg = $cck.col2tok(%w,32,1,32,$hget(%hc,KickMsg.MaxDataLength),</cck>)
      if (!%actdone) { cck.act KICK $network %nick %addr $cck.actmsg(KICK,$network,%nick,%msg) }
    }
  }
  elseif ($fline(%w,& WARN,1)) {
    if (!%actdone) {
      filter -wwc %w %w & WARN
      var %msg = $cck.col2tok(%w,32,1,32,$hget(%hc,WarnMsg.MaxDataLength),</cck>)
      cck.act WARN $network %nick %addr $cck.actmsg(WARN,$network,%nick,%msg)
    }
  }
  elseif ($fline(%w,& ASK,1)) {
    filter -wwc %w %w & ASK
    var %msg = $cck.col2tok(%w,32,1,32,$hget(%hc,AskMsg.MaxDataLength),</cck>)
    if (!$cck.DiaASK.get($network,%nick)) {
      cck.mychans $network %nick echo -t 10 $+ $network %nick %addr on %msg $+ . Press $cck.key.info
    }
    if ($cck.onchan(*,$network,%nick)) {
      cck.diaASK.add $iif(%foundbad,$chr(35),$) $network %nick %addr $cck.col2tok(%w,32,1,32,$hget(%hc,DiaASK.MaxDataLength),</cck>)
      cck.badsound
    }
  }
  cck.rawclean
}

;===================================================================================================
; IRC / JOIN
;===================================================================================================

on *:JOIN:#: {
  cck.CheckNicks.AddDataGlobal $network HOST $site NICK $nick
  if ($nick == $me) {
    cck.join.process $chan SUCCESS
    if ($cck.CheckChans.Check($network,$chan,ANY)) {
      hadd cck.H $s2c($network JOIN $chan) 1
      cck.queue.add $network MODE $chan +b - CHECKCHANS-OWNJOIN
    }
  }
  if ($cck.Bans.CheckUser($network,$chan,$fulladdress)) { 
    cck.queue.add $network MODE $chan +b $wildsite CHECKCHANS-BANS
    cck.queue.add $network KICK $chan - $nick CHECKCHANS-BANS BlackListed
  }
  if (!$cck.CheckChans.Check($network,$chan,USERJOIN)) { return }
  if ($cck.exclude($network,$nick)) || ($istok($replace($hget(cck.HConfig,ExcludeNicks),<ME>,$me),$nick,32)) { return }
  cck.queue.add $network WHOIS $nick - $address CHECKCHANS-USERJOIN
}
RAW 366:*:{
  ; cck.CheckNicks.AddDataGlobal $network CHANNEL $2 USERS $nick($2,0)
  var %s = $s2c($network JOIN $2) | if (!$hget(cck.H,%s)) { return } | hdel cck.H %s
  if ($cck.CheckChans.Check($network,$2,OWNJOIN)) { cck.whoischan $network $2 CHECKCHANS-OWNJOIN }
}
#cck.raw.join off
RAW 384:*:{ cck.join.process $2 CBAN }
RAW 403:*:{ cck.join.process $2 NOSUCHCHAN }
RAW 405:*:{ cck.join.process $2 TOOMANY }
RAW 439:*:{ if ($left($2,1) isin $chantypes) { cck.join.process $2 TOOFAST } }
RAW 470:*:{ cck.join.process $2 LIMITED-LINKED $wildtok($3-,$+($left($2,1),*),2,32) }
RAW 471:*:{ cck.join.process $2 LIMITED }
RAW 473:*:{ cck.join.process $2 INVITE-ONLY }
RAW 474:*:{ cck.join.process $2 BANNED }
RAW 475:*:{ cck.join.process $2 NEEDKEY }
RAW 476:*:{ cck.join.process $2 BADCHANMASK }
RAW 477:*:{ if ($istok($3-,join,32)) { cck.join.process $2 NEEDREGISTERED } }
RAW 479:*:{ cck.join.process $2 GLINED }
RAW 485:*:{ if ($3-5 == Cannot join channel) { cck.join.process $2 FREEREASON $remove($6,$chr(40),$chr(41)) $2 } }
RAW 489:*:{ cck.join.process $2 CHANNEL+Z }
RAW 490:*: { cck.join.process $2 NEEDSSL }
RAW 495:*: { cck.join.process $2 DELAYEDJOINAFTERKICK $2 }
RAW 500:*: { cck.join.process $2 TOOMANYJOINS $2 }
RAW 520:*: { cck.join.process $2 NEEDIRCOP }
RAW 530:*: { cck.join.process $2 NEWONLYBYIRCOPS $2 }
on *:SNOTICE:*** Can not join * Channel Reserved For Services: { 
  cck.join.process $gettok($5,1,58) RESERVEDFORSERVICES
}
#cck.raw.join end
; IN32 $1 = channel $2 = result flag
alias cck.join.process {
  if (!$cck.raw(JOIN,$1,*,*)) { return }
  var %s = $cck.queue.get($network,JOIN,$1,*,*) | if (!%s) { cck.rawclean | return }
  var %c = $1, %result = $2 | tokenize 32 %s | var %pr = $1, %addr = $6, %sys = $gettok($7,1,59), %sysd = $gettok($7,2-,59)
  if ($istok(%sys,CHECKNICKS,45)) {
    if (%result == SUCCESS) { cck.CheckNicks.HuntSwitchChan $network %c %sysd }
    elseif (%result == TOOMANY) {

    }
  }
  elseif (%result != SUCCESS) { cck.echo- SYSTEM: JOIN12 $1 failed ( $+ $2 $+ ). }

  cck.rawclean
}

;===================================================================================================
; IRC / PART
;===================================================================================================

on *:PART:#: {
  cck.CheckNicks.AddDataGlobal $network HOST $site NICK $nick
  if ($nick == $me) {
    if (!$cck.rawclean(PART,#,-,-)) { cck.queue.rem $network PART # - - }
    cck.diaASK.clean $network #
  }
  elseif (!$cck.onchan(*,$network,$nick,#)) {
    if ($hget(cck.HConfig,NoAbort@PART-KICK) != ON) { cck.queue.rem $network WHOIS $nick - & }
    cck.diaASK.rem $network $nick
  }
  cck.queue.rem $network KICK # & $nick
  cck.queue.rem $network MODE # & $nick
}
#cck.raw.part off
; You're not on that channel
RAW 442:*: { cck.rawclean PART $2 - - }
; No such channel
RAW 403:*: { cck.rawclean PART $2 - - }
#cck.raw.part end

;===================================================================================================
; IRC / KICK
;===================================================================================================

on *:KICK:#: {
  if ($knick == $me) { cck.diaASK.clean $network # }
  elseif (!$cck.onchan(*,$network,$knick,$chan)) {
    if ($hget(cck.HConfig,NoAbort@PART-KICK) != ON) { cck.queue.rem $network WHOIS $knick - * }
    cck.diaASK.rem $network $knick
  }
  if (!$cck.rawclean(KICK,#,-,$knick)) { cck.queue.rem $network KICK # & $knick }
  cck.queue.rem $network MODE # & $knick
}
#cck.raw.kick off
; <channel> You're not channel operator
RAW 482:*: { cck.rawclean KICK $2 * * }
; <flag> <nick> is a channel admin/owner/network service
RAW 974:*: { cck.rawclean KICK * * $2 }
; <nickname> <channel> :They aren't on that channel
RAW 441:*: { cck.raw KICK $3 - $2 }
#cck.raw.kick end

;===================================================================================================
; IRC / MODE
;===================================================================================================

on *:RAWMODE:#: {
  cck.ChanModeParse $network $1-
  var %w = $result, %total = $line(%w,0), %i = 1
  while (%i <= %total) { cck.mode.process SET $gettok($line(%w,%i),2-,32) | inc %i }
  window -c %w
}
; <channel> :End of Channel Ban List.
RAW 368:*:{
  cck.rawclean MODE $2 +b -
  cck.Bans.Clean $network $2
}
#cck.raw.mode off
; <channel> You're not channel operator
RAW 482:*: { if ($cck.raw(MODE,$2,*,*)) { cck.mode.process NOOP } }
; <flag> <nick> is a channel admin/owner/network service
RAW 974:*: { if ($cck.raw(MODE,*,? $+ $2,$3)) { cck.mode.process PROTECTED } }
; <nickname> <channel> :They aren't on that channel
RAW 441:*: { cck.rawclean MODE $3 * $2 }
<channel> <banmask> :Channel ban/ignore list is full
RAW 478:*: { if ($cck.raw(MODE,$2,?b,$3)) { cck.mode.process FULL } }
; <channel> <ban> <nick> <ctime>
RAW 367:*:{ cck.raw MODE $2 +b - }
; <channel> <exception mask>
RAW 348:*:{ cck.raw MODE $2 +e - }
; <channel> :End of Channel Exception List
RAW 349:*:{

  cck.rawclean MODE $2 +e -
}
; <channel> <invite mask>
RAW 346:*:{ cck.raw MODE $2 +I - }
; <channel> :End of Channel Invite List
RAW 347:*:{

  cck.rawclean MODE $2 +I -
}
#cck.raw.mode end

; Input: $1 = SET or NOOP or PROTECTED or FULL $2 = modeflag $3 = modetextlabel|UNKNOWN $4 = nick or mask
; $2- only in case SET
alias -l cck.mode.process {
  var %status = $1
  goto %status

  :SET
  var %flag = $2, %mode = $3, %par = $4
  if ($istok(OP HALFOP VOICE,%mode,32)) {
    if ($cck.exclude($network,%par)) { cck.queue.rem $network WHOIS %par - * }
  }
  if (!$cck.rawclean(MODE,#,%flag,%par)) { cck.queue.rem $network MODE # %flag %par }
  return
  :NOOP | :PROTECTED | cck.rawclean | return

  :FULL

  cck.rawclean
}

;===================================================================================================
; IRC / LIST
;===================================================================================================

#cck.raw.list off
; Channel :Users Name
RAW 321:*:{ if ($cck.raw(LIST,*,-,-)) { cck.list.close@ } }
; <channel> <users> :<topic>
RAW 322:*:{ if ($cck.raw(LIST,*,-,-)) { cck.list.close@ | aline @cck.raw $2- } }
; :End of /LIST
RAW 323:*:{ if ($cck.raw(LIST,*,-,-)) { cck.list.close@ | cck.list.process } }
alias cck.list.process {
  var %w = @cck.raw, %c = $gettok($hget(cck.H,QBUSY),3,32)
  var %i = 1 | while ($line(%w,%i)) { var %rec = $v1 | if ($gettok(%rec,1,32) == %c) { tokenize 32 %rec | var %users = $2, %topic = $3- | goto next } | inc %i }
  var %users = 0, %topic = ?
  :next 
  var %rec = $cck.queue.get($network,LIST,%c,-,-) | if (!%rec) { cck.rawclean | return }
  tokenize 32 %rec | var %sysdata = $7, %sys = $gettok($7,1,59), %sysd = $gettok($7,2-,59), %w = @cck.raw
  tokenize 59 %sysd | var %nick = $1, %addr = $2, %p3 = $3
  if ($istok(%sys,TRACKUSERS,45)) { cck.echoLog $+(04,$network %nick,) CHANNEL: $+(%p3,%c) USERS: %users TOPIC: %topic }
  elseif ($istok(%sys,CHECKNICKS,45)) { noop }
  else {
    if ($hget(cck.HConfig,WinLog.ShowNonMatches) == ON) {
      if (!$istok(%sys,CHECKNICKS,45)) && (!$istok(%sys,CONTINUE,45)) && (!$istok(%sys,TIMER,45)) {
        cck.echoLog $+(10,$network %nick,) CHANNEL: $+(%p,%c) USERS: %users TOPIC: %topic
      }
    }
  }
  cck.CheckNicks.AddDataGlobal $network CHANNEL %c USERS %users
  cck.CheckNicks.AddDataGlobal $network CHANNEL %c TOPIC %topic
  cck.rawclean
}
#cck.raw.list end
alias -l cck.list.close@ { if ($window(channels list)) { window -c "channels list" } }

;===================================================================================================
; IRC COMMAND QUEUE
;===================================================================================================
; hash per priority level
; item: network,command,par1,switch,par2
; data: system;data1;data2;dataX message

; +++ QUEUE / EVENTS CHECK
; Queue format:
; Item: <network>,<WHOIS|JOIN|PART|MODE|KICK|LIST|MSG|NOTICE|DESCRIBE>,<nick|channel>,<switch|->,<whoisnick-user@host|pass|nick|banmask|->
; Data: <systemdata> <kickmsg|partmsg|message>

; :Too many lines in your output
RAW 416:*: { 
  if ($cck.raw(LIST,*,-,-)) { cck.list.process }

}
; :Server load is temporarily too heavy. Please wait a while and try again.
RAW 263:*: { if ($cck.raw(*,*,*,*)) { cck.queue.recover NOWIPE } }

; +++ QUEUE / FORCED RESPONSE CASE MSG|NOTICE|DESCRIBE
#cck.raw.ping off
on ^*:PONG: { if ($network $2 == $hget(cck.H,QPING)) { haltdef | cck.rawclean } }
#cck.raw.ping end

; +++ QUEUE / CHECK IF GIVEN EVENT PARAMETERS MATCH THE CURRENT BUSY (current connection)
; IN32 $1 = command $2 = par1 $3 = switch $4 = par2
alias cck.raw { cck.rawwhoisscandinav $2 | if ($network $1- * iswm $hget(cck.H,QBUSY)) { haltdef | return 1 } | return 0 }
; +++ QUEUE / CHECK + CLEANUP RAW DATA
; IN32 see cck.raw, direct call in case just one response (optional)
alias cck.rawclean {
  cck.rawwhoisscandinav $2 | if ($1 != $null) && ($network $1- * !iswm $hget(cck.H,QBUSY)) { return 0 }
  haltdef | hdel -w cck.HRaw * | clear @cck.raw
  tokenize 32 $hget(cck.H,QBUSY) | hdel cck.H QBUSY | .disable cck.raw. $+ $2
  if ($2 != PING) { cck.queue.rem $1-5 }
  .timercck.Timeout OFF | .timercck.DeadGap -oih 1 $hget(cck.HConfig,DeadGap) cck.queue.donext
  return 1
}
; +++ QUEUE / SPECIAL CHECK
; Because of IRC's scandanavian origin, the characters {}| are considered to be the lower case equivalents of the characters []\, respectively.
; IN32 $1 = nick
alias cck.rawwhoisscandinav {
  var %nick = $1 | tokenize 32 $hget(cck.H,QBUSY) | if ($network != $1) || ($2 != WHOIS) { return }
  if ($replace($3,$chr(123),[,$chr(125),],|,\) == $replace(%nick,$chr(123),[,$chr(125),],|,\)) { haltdef }
}

; +++ GET PRIORITY FOR COMMAND-SYSTEM-DATA
; IN32: $1 = command $2 = sysdata
; Call only as identifier.
alias cck.queue.getpriority {
  var %j = $+($1,.,$gettok($2,1,59)) | goto %j 

  :WHOIS.CHECKCHANS-TIMER | return 1
  :WHOIS.CHECKCHANS-CONTINUE | return 1
  :WHOIS.CHECKCHANS-USERJOIN | return 2
  :WHOIS.CHECKCHANS-OWNJOIN | return 3
  :WHOIS.UIL | return 4
  :WHOIS.UIR | return 4
  :WHOIS.CHECKNICKS-TIMEPOLLING | return 4
  :WHOIS.CHECKNICKS-TIMERDEFAULT | return 4
  :WHOIS.CHECKNICKS-UIL | return 4
  :WHOIS.CHECKCHANS-UIL | return 4
  :MSG.CHECKCHANS-ACTION | return 4
  :MODE.CHECKCHANS-BANS | return 4
  :LIST.CHECKNICKS-HUNT | return 5
  :LIST.CHECKNICKS-UIL | return 5
  :LIST.TRACKUSERS | return 5
  :LIST.UIL | return 5
  :LIST.UIR | return 5
  :MODE.CHECKCHANS-OWNJOIN | return 6
  :KICK.CHECKCHANS-ACTION | return 7
  :MODE.CHECKCHANS-ACTION | return 8
  :JOIN.CHECKCHANS-UIL | return 8
  :JOIN.CHECKNICKS-HUNT | return 8
  :PART.CHECKCHANS-UIL | return 8

  :%j | return 1
}
alias cck.queue.priorities { return 9 }

; +++ QUEUE / ADD
; IN32 $1 = network or * $2 = command $3 = par1 $4 = switch $5 = par2 $6 = sysdata [$7- = msg]
alias cck.queue.add {
  var %netw = $1, %pr = $cck.queue.getpriority($2,$6)
  if ($1 != *) { var %net = $1 | goto single }
  var %i = 0
  :next | inc %i | if (!$scon(%i)) { cck.queue.donext | return }
  if ($scon(%i).status != connected) || (%netw !iswm $scon(%i).network) { goto next }
  var %net = $scon(%i).network
  :single
  if ($hget(cck.H,QBUSY) == %net $2-5) { goto next }
  var %rec = $cck.queue.get(%net,$2,$3,$4,$5) | if (%rec) && ($gettok(%rec,1,32) >= %pr) { return }
  var %4 = $chr(44), %hi = $+(%net,%4,$2,%4,$3,%4,$4,%4,$5)
  if (%rec) { hdel cck.HQ $+ %pr %hi }
  hadd cck.HQ $+ %pr %hi $6- | if (%netw = *) { goto next }
  cck.queue.donext
}

; +++ QUEUE / GET DATA
; IN32 $1 = network $2 = command $3 = par1 $4 = switch $5 = par2 (wildcards allowed)
; OUT32 $1- = 0 (notfound) or $1- = priority hash-item data, all space separated
alias cck.queue.get {
  var %4 = $chr(44), %wm = $+($1,%4,$2,%4,$3,%4,$4,%4,$5), %i = 1, %h, %prt = $cck.queue.priorities
  while (%i <= %prt) {
    %h = cck.HQ $+ %i
    if ($hfind(%h,%wm,1,w)) { return %i $replace($v1,$chr(44),$chr(32)) $hget(%h,$v1) }
    inc %i
  }
  return 0
}

; +++ QUEUE / GET AMOUNT QUEUE ENTRIES FOR GIVEN COMMAND+SWITCH
; IN32 $1 = command $2 = switch
alias cck.queue.get# {
  var %4 = $chr(44), %wm = $+(*,%4,$1,%4,*,%4,$2,%4,*), %i = $cck.queue.priorities, %count = 0
  while (%i) { inc %count $hfind(cck.HQ $+ %i,%wm,0,w) | dec %i }
  return %count
}

; +++ QUEUE / CHECK IF QUEUE IS EMPTY FOR GIVEN COMMAND+SWITCH
; IN32 $1 = command $2 = switch
; OUT32 $1 = 0 (not empty) or 1 (empty)
alias cck.queue.empty {
  var %4 = $chr(44), %wm = $+(*,%4,$1,%4,*,%4,$2,%4,*), %i = $cck.queue.priorities
  while (%i) { if ($hfind(cck.HQ $+ %i,%wm,1,w)) { return 0 } | dec %i }
  return 1
}

; +++ QUEUE / CHECK IF THE BUSY ONE IS STILL IN A QUEUE HASH, IF NOT, REMOVE BUSY FLAG AND DO NEXT
; to be done after removal(s)
alias cck.queue.checkbusy {
  cck.diaMAIN.title | cck.diaASK.title
  var %hi = $hget(cck.H,QBUSY) | if (!%hi) || ($numtok(%hi,32) == 1) { return }
  tokenize 32 %hi | if (!$cck.queue.get($1,$2,$3,$4,$5)) { hdel cck.H QBUSY | cck.queue.donext }
}

; +++ QUEUE / DO NEXT
; walk from hashX to hash1 (priority into effect)
alias cck.queue.donext {
  cck.diaMAIN.title | cck.diaASK.title
  if ($cck.queue.process != ON) || ($hget(cck.H,QBUSY)) || (($timer(cck.DeadGap) && ($ctimer != cck.DeadGap))) { return }
  var %i1 = $cck.queue.priorities, %i2 = 1, %hi, %hv, %cid, %s, %flag
  :next
  if ($hget(cck.HQ $+ %i1,%i2).item) { goto found }
  dec %i1 | if (%i1) { %i2 = 1 | goto next }
  cck.CheckChans.Sweep CONTINUE | return
  :found
  %hi = $v1 | %hv = $hget(cck.HQ $+ %i1,%hi) | tokenize 44 %hi
  %cid = $cck.net2cid($1) | if (!%cid) { goto del }
  if (%cid != $cid) { scid %cid }
  goto $2

  :WHOIS | $2 $iif($hget(cck.HConfig,DetailedWhois) == ON,$3 $3,$3) | .enable #cck.raw.whois | goto timeout
  :JOIN | if ($me !ison $3) { $2-4 | .enable #cck.raw.join | goto timeout } | goto del
  :PART | if ($me ison $3) { $2-3 $gettok(%hv,2-,32) | .enable #cck.raw.part | goto timeout } | goto del
  :MODE
  if ($me !isop $3) { goto del }
  %flag = $remove($4,+,-) | if (%flag !isincs $chanmodes) { goto del }
  if ($5 == -) {
    if (%flag isincs $chan($3).mode) { goto del }
    $2-4 | .enable #cck.raw.mode | goto timeout
  }
  %s = $istokcs(b e I,%flag,32)
  if (!%s) { if ($5 !ison $3) { goto del } | $2-5 | .enable #cck.raw.mode | goto timeout }
  if ($4 === +b) { if ($5 isban $3) { goto del } }
  elseif ($4 === -b) { cck.Bans.Rem $1 $3 $5 | if ($5 !isban $3) { goto del } }
  elseif ($4 === +e) { if ($5 isexcept $3) { goto del } }
  elseif ($4 === -e) { if ($5 !isexcept $3) { goto del } }
  elseif ($4 === +I) { if ($5 isinvite $3) { goto del } }
  elseif ($4 === -I) { if ($5 !isinvite $3) { goto del } }
  $2-5 | .enable #cck.raw.mode | goto ping
  :KICK | if ($me !isop $3) { goto del } | $2-3 $5 $gettok(%hv,2-,32) | .enable #cck.raw.kick | goto timeout
  :LIST | $2-3 | .enable #cck.raw.list | goto timeout
  :MSG | :NOTICE | :DESCRIBE
  if ($left($3,1) isin $chantypes) && ($me !ison $3) { goto del }
  $2-3 $gettok(%hv,2-,32) | goto ping

  :del | hdel cck.HQ $+ %i1 %hi | cck.diaMAIN.title | cck.diaASK.title | goto next
  :ping | hdel cck.HQ $+ %i1 %hi
  if ($hget(cck.HConfig,UsePING) != ON) { .timercck.DeadGap -oih 1 $hget(cck.HConfig,NoPINGGap) cck.queue.donext }
  else { var %s = cck. $+ $ticks | scid %cid .quote PING %s | hadd cck.H QPING $1 %s | .enable #cck.raw.ping }
  :timeout | hadd cck.H QBUSY $1- %hv | .timercck.Timeout -oi 1 $hget(cck.HConfig,Timeout) cck.queue.timeout $1- %hv | return
  :error | if ($gettok($error,1-2,32) == * /list) { reseterror | inc %i2 | goto next }
}
alias cck.queue.timeout { if ($1- == $hget(cck.H,QBUSY)) { cck.queue.recover } }
; $1 = NOWIPE (optional, will NOT wipe the current entry so is gonna retry)
alias cck.queue.recover {
  if ($hget(cck.H,QBUSY) == $null) { return }
  tokenize 32 $v1 | hdel cck.H QBUSY | hdel cck.H QPING | .disable cck.raw. $+ $2
  if ($1 != NOWIPE) { cck.queue.rem $1-5 }
  hdel -w cck.HRaw * | clear @cck.raw | cck.queue.donext
}

; +++ QUEUE / UPDATE NICK
; IN32 $1 = network $2 = old nick $3 = new nick
alias cck.queue.updnick {
  var %wm = $+($1,$chr(44),*), %n0 = $2, %n1 = $3, %i1 = 1, %h, %i2, %hr = $rhash, %4 = $chr(44), %hi, %hv, %c, %j, %prt = $cck.queue.priorities
  while (%i1 <= %prt) {
    %h = cck.HQ $+ %i1 | %i2 = 1
    while ($hfind(%h,%wm,%i2,w)) {
      %hi = $v1 | %hv = $hget(%h,%hi) | tokenize 44 %hi | %j = $2 | goto %j

      :WHOIS | :MSG | :NOTICE | :DESCRIBE
      if ($3 === %n0) { hadd %hr $+(%i1,%4,$puttok(%hi,%n1,3,44)) %hv | goto del }
      inc %i2 | continue
      :MODE
      if ($gettok($5,1,33) === %n0) { hadd %hr $+(%i1,%4,$puttok(%hi,$puttok($5,%n1,1,33),5,44)) %hv | goto del }
      inc %i2 | continue
      :KICK
      if ($5 === %n0) { hadd %hr $+(%i1,%4,$puttok(%hi,%n1,5,44)) %hv | goto del }
      inc %i2 | continue
      :%j | inc %i2 | continue

      :del | if ($gettok($hget(cck.H,QBUSY),1-5,32) != $replace(%hi,$chr(44),$chr(32))) { hdel %h %hi } | else { inc %i2 }
    }
    inc %i1
  }
  %i1 = 1
  while ($hget(%hr,%i1).item) {
    hadd cck.HQ $+ $gettok($v1,1,44) $gettok($v1,2-,44) $hget(%hr,$v1)
    inc %i1
  }
  hfree %hr
}

; +++ QUEUE / REMOVE
; IN32 $1 = network $2 = command $3 = par1 $4 = switch $5 = par2 (wildcards allowed)
alias cck.queue.rem {
  var %4 = $chr(44), %wmi = $+($1,%4,$2,%4,$3,%4,$4,%4,$5), %i = 1, %h, %prt = $cck.queue.priorities
  while (%i <= %prt) {
    %h = cck.HQ $+ %i | while ($hfind(%h,%wmi,1,w)) { hdel %h $v1 }    
    inc %i
  }
}

; +++ QUEUE / WIPE ALL FOR GIVEN NETWORK
; IN32 $1 = network
alias cck.queue.remnet {
  var %i = 1, %prt = $cck.queue.priorities | while (%i < %prt) { hdel -w cck.HQ $+ %i $1 $+ ,* | inc %i }
  cck.queue.checkbusy
}

; +++ QUEUE / GET STATUS
alias cck.queue.getstatus {
  var %i = $cck.queue.priorities, %list
  while (%i) { if ($hget(cck.HQ $+ %i,0).item) { %list = %list $+(%i,:,$v1) } | dec %i }
  return $iif(%list,%list,empty)
}

; +++ QUEUE / PROCESSING
; IN32 $1 = ON or OFF (optional, if not specified, status ON/OFF will be returned) $2 = SILENT (optional - no report)
alias cck.queue.process {
  if (!$1) { return $hget(cck.H,QPROC) }
  if ($hget(cck.H,QPROC) == $1) { return }
  goto $1
  :ON
  hadd cck.H QPROC ON | cck.diaMAIN.upd Session QueueProcess ON | cck.queue.donext
  if (!$2) { cck.echo+ QUEUE: Processing ON. } | return
  :OFF
  hadd cck.H QPROC OFF | cck.diaMAIN.upd Session QueueProcess OFF
  if (!$2) { cck.echo+ QUEUE: Processing OFF. } | return
}

;===================================================================================================
; IRC FUNCTION BASE
;===================================================================================================

; +++ FUNCTION / WHOIS CHANNEL ON EVERY NETWORK 
; IN32 $1 = channel $2 = sysdata
alias cck.whoischanglobal {
  var %i = 1
  while ($scon(%i)) {
    if ($scon(%i).status == connected) { cck.whoischan $scon(%i).network $1- }
    inc %i
  }
}

; +++ FUNCTION / WHOIS CHANNEL
; IN32 $1 = network $2 = channel $3 = sysdata
alias cck.whoischan {
  var %cid = $cck.net2cid($1) | if (!%cid) { return } | if ($cid != %cid) { scid %cid }
  if ($me !ison $2) { return }
  var %i = 1, %list = $replace($hget(cck.HConfig,ExcludeNicks),<ME>,$me), %s = $iif($istok($gettok($3,1,59),UIL,45),1,0)
  while ($nick($2,%i)) {
    var %n = $v1 | if ($istok(%list,%n,32)) { inc %i | continue }
    if (!%s) && ($cck.exclude($1,%n)) { inc %i | continue }
    cck.queue.add $1 WHOIS %n - $iif($address(%n,0),$gettok($v1,2-,33),-) $3
    inc %i
  }
}

; +++ FUNCTION / WHOIS NICK ON EVERY NETWORK 
; IN32 $1 = nick $2 = sysdata
alias cck.whoisnickglobal {
  var %i = 1
  while ($scon(%i)) {
    if ($scon(%i).status == connected) { cck.whoisnick $scon(%i).network $1- }
    inc %i
  }
}

; +++ FUNCTION / WHOIS NICK
; IN32 $1 = network $2 = nick $3 = sysdata
alias cck.whoisnick {
  var %cid = $cck.net2cid($1) | if (!%cid) { return } | if ($cid != %cid) { scid %cid }
  var %list = $replace($hget(cck.HConfig,ExcludeNicks),<ME>,$me)
  if ($istok(%list,$2,32)) { return }
  if ($gettok($gettok($3,1,59),1,45) != CheckNicks) && (!$iif($istok($gettok($3,1,59),UIL,45),1,0)) && ($cck.exclude($1,$2,$3)) { return }
  cck.queue.add $1 WHOIS $2 - $iif($address($2,0),$gettok($v1,2-,33),-) $3
}

; +++ FUNCTION / CHECK IF NICK SHOULD BE WHOISSED 
; IN32 $1 = @ or % or + or * $2 = network $3 =nick $4 = optional channel to force as not present (mIRC IAL not updated yet) or * (no CheckChans presence check)
; $1 is to exclude Operators/Halfops/Voices, * means don't exclude them
; OUT32 $1 = 1 (check user|user present) or 0 (don't check user|user not present)
alias cck.onchan {
  var %cid = $cck.net2cid($2) | if (!%cid) { return 0 } | if ($cid != %cid) { scid %cid }
  var %i = 1, %x = $1, %count = 0
  while ($comchan($3,%i)) {
    var %c = $v1 | if (%c == $4) { inc %i | continue }
    if (%c != *) && (!$cck.CheckChans.Check($2,%c,ANY)) { inc %i | continue }
    if (@ isin %x) { if ($3 isop %c) { return 0 } }
    if (% isin %x) { if ($3 ishop %c) { return 0 } }
    if (+ isin %x) { if ($3 isvoice %c) { return 0 } }
    inc %count | inc %i
  }
  if (%count) { return 1 } | return 0
}

; +++ FUNCTION / GET LIST OF THE SCRIPT-ACTIVE CHANNELS FOR THE GIVEN NICK / PERFORM COMMAND ON THEM
; Requires also +o
; IN32 $1 = network $2 = nick $3 = command $4 = -switches or <none> $5 = parameters ($3- optional)
; OUT32 $result = list channels
alias cck.mychans {
  var %cid = $cck.net2cid($1) | if (!%cid) { return 0 } | if ($cid != %cid) { scid %cid }
  var %i = 1, %list, %c, %list
  while ($comchan($2,%i)) {
    %c = $v1
    if ($me !isop %c) || (!$cck.CheckChans.Check($1,%c,ANY)) { inc %i | continue }
    %list = %list %c | if ($3 != $null) { $3 $iif($4 != <none>,$4,$null) %c (04CCK) $5- }
    inc %i
  }
  return %list
}

; +++ FUNCTION / GET TAG-REPLACED WARN OR KICK MESSAGE FOR GIVEN USER
; IN32 $1 = WARN or KICK or BAN $2 = network $3 = nick $4- = warn or kick message
alias cck.actmsg {
  var %act = $iif($1 == BAN,KICK,$1)
  return $replace($hget(cck.HConfig,%act $+ Msg),<BADCHANS>,$4-,<MYCHANS>,$cck.mychans($2,$3))
}

; +++ FUNCTION / PERFORM ACTION ON USER
; IN32 $1 = WARN or KICK or BAN $2 = network $3 = nick $4 = user@host $5- = warn/kickmsg
; OUT32 $1 = amount added
alias cck.act {
  var %cid = $cck.net2cid($2) | if (!%cid) { return 0 } | if ($cid != %cid) { scid %cid }
  var %i = 1, %a = *!*@ $+ $gettok($4,2,64), %count = 0
  while ($comchan($3,%i)) {
    var %c = $v1
    if ($1 == WARN) { cck.queue.add $2 MSG $3 - - CHECKCHANS-ACTION $5- | return 1 }
    elseif ($1 == BAN) { cck.Bans.Add $2 %c %a $hget(cck.HConfig,BanDuration) $5- | if ($me isop %c) { cck.kickclones $2 %c %a $5- } }
    elseif ($1 == KICK) { if ($me isop %c) { cck.kickclones $2 %c %a $5- } }
    inc %i
  }
  return %count
}

; +++ FUNCTION / KICK CLONES
; IN32 $1 = network $2 = channel $3 = mask $4- = kickmsg
; Output: $result = amount kicks
alias cck.kickclones {
  var %cid = $cck.net2cid($1) | if (!%cid) { return 0 } | if ($cid != %cid) { scid %cid }
  if ($me !isop $2) { return 0 }
  var %i = 1 | while ($ialchan($3,$2,%i).nick) { cck.queue.add $1 KICK $2 - $v1 CHECKCHANS-ACTION $4- | inc %i }
  return $calc(%i - 1)
}

; +++ FUNCTION / SPLIT WHOIS CHANNELPREFIXES FROM CHANNELNAME (current connection)
; IN32 $1 = network $2 = [prefixes]<channelname>
; OUT32 $result = INVALID or $1 = prefixes or r (none) $2 = channelname
; notes:
; prefix < = channel mode +D and the whoissed user is hidden
; prefix - = when user mode +d,  doesnt receive text notice action (channel PRIVMSG)
; prefix ! = when your server has accepted part/kick but the other server hasn't, when the servers aren't synced at the exact time you do a whois
alias cck.getprefixchan {
  var %cid = $cck.net2cid($1) | if (!%cid) { return INVALID } | if ($cid != %cid) { scid %cid }
  var %total = $len($2), %i = 1
  while (%i <= %total) {
    var %char = $mid($2,%i,1) | if (%char isin $prefix $+ -<!) { inc %i | continue }
    if (%char !isin $chantypes) { inc %i | continue }
    var %chan = $mid($2,%i) | if (%i == 1) { return r %chan } | else { return $left($2,$calc(%i - 1)) %chan } 
    inc %i
  }
  return INVALID
}

; +++ FUNCTION / GET CID VALUE FOR GIVEN NETWORK
alias cck.net2cid {
  var %total = $scon(0), %i = 1
  while (%i <= %total) { if ($scon(%i).network == $1) && ($scon(%i).status == connected) { return $scon(%i) } | inc %i }
  return 0
}

; +++ FUNCTION / PARSE RAW MODE STRING
; Input: $1 = network $2- = raw mode string
; Output: $result = NOTFOUND or $1 = name of @window layout:
; case USER: USER <modeflag> <modetextlabel|UNKNOWN> <nick or mask>
; case CHAN: CHAN <modeflag> <modetextlabel|UNKNOWN> <optional value>
alias cck.ChanModeParse {
  var %cid = $cck.net2cid($1) | if ($cid != %cid) { scid %cid }
  var %net = $1, %rmf = $2, %rmp = $3-, %total = $len(%rmf), %i = 1, %wr = $rwin, %set, %ipar = 0
  while (%i <= %total) {
    var %c = $mid(%rmf,%i,1)
    if ($istok(+ -,%c,32)) { var %set = %c | inc %i | continue }
    if (!%set) { inc %i | continue }
    var %type = $cck.ChanModeType(%net,%c) | if (!%type) { inc %i | continue }
    var %flag = $+(%set,%c), %mode =  $cck.flag2mode(%flag) | if (!%mode) { var %mode = UNKNOWN }
    goto %type
    :A | inc %ipar | aline %wr USER %flag %mode $gettok(%rmp,%ipar,32) | inc %i | continue
    :B | inc %ipar | aline %wr CHAN %flag %mode $gettok(%rmp,%ipar,32) | inc %i | continue
    :C
    if (%set == +) { inc %ipar | aline %wr CHAN %flag %mode $gettok(%rmp,%ipar,32) } | else { aline %wr CHAN %flag %mode }
    inc %i | continue
    :D | aline %wr CHAN %flag %mode | inc %i | continue
  }
  return %wr
}

; +++ FUNCTION / CHANNEL MODES - GET TYPE
; Input: $1 = network $2 = flag
; Output: $result = 0 or $1 = A or B or C or D
alias cck.ChanModeType {
  var %net = $1, %flag = $2, %cid = $cck.net2cid($1) | if ($cid != %cid) { scid %cid }
  var %i = 1
  while ($gettok($chanmodes,%i,44) != $null) {
    if (%flag isincs $v1) {
      var %type = $gettok(A B C D,%i,32)
      if (%type) { return %type }
      return 0
    }
    inc %i
  }
  return 0
}

; +++ FUNCTION / MODE FLAG <-> NAME CONVERSION
alias cck.mode2flag { return $gettok($cck.flaglist,$findtok($cck.modelist,$1,1,32),32) }
alias cck.flag2mode { return $gettok($cck.modelist,$findtokcs($cck.flaglist,$1,1,32),32) }
alias cck.modelist {
  return OP DEOP HALFOP DEHALFOP VOICE DEVOICE BAN UNBAN EXCEPT UNEXCEPT INVEX DEINVEX ADMIN DEADMIN OWNER DEOWNER $&
    MODERATED DEMODERATED KEYED UNKEYED SECRET UNSECRET
}
alias cck.flaglist { return +o -o +h -h +v -v +b -b +e -e +I -I +a -a +q -q +m -m +k -k +s -s }

Comments

Sign in to comment.
Azrael-x   -  Aug 04, 2014

Qc This script look very useful , but i have some problem to use it . You can do a little tutorial to explain all the fuctions and how to use it? For example i want to know how to track an user by idle time but i really don't understand how to do it . you can do some examples ? thanks for your time !

 Respond  
Qc   -  Feb 18, 2012

1.13 Fixed various bugs and little improvements.

 Respond  
Qc   -  Jan 24, 2010

Well, with version 1.12, this ChanCheck project has reached the overall functionality I had in mind when I started it back in summer 2009.
However, if you need help, found a bug, or even find some feature (within the scope of the scripts purpose) is missing, don't hesitate to bring it up.

 Respond  
Qc   -  Aug 08, 2009

If half the people of (your?) chat servers have bad words in their whois, and you have no problem with it, then you do not need a script.
If that isn't obvious, ofcourse. :)

The scope of this script is far beyond bad/swear words in whois.
You want to know more about the people visiting your channels?
You want to know on which other channels they are, maybe to visit them too?
Your channel is pestered by a bunch lamers and they all seem to be on a certain channel (whatever it is named, bad/swear words or not)?
You want to keep track of all people that are op on other channels?

This script is essentially nothing more than a versatile filter that automatically grabs on request-available data for you, filters it according to your information needs, and can act, if desired. It gives you a much higher awareness of the people on your channels. And that is its purpose. 'Check' Channels.

 Respond  
Cracker200   -  Aug 08, 2009

Um.... where is the forums lmao? NVM lol i found it but how do i post on it ???? :) sorry i am a n00b to this site xD

 Respond  
Atr   -  Aug 08, 2009

@Cracker200, try the forums :)

 Respond  
Cracker200   -  Aug 08, 2009

Errm Can some one tell me where i post if i need some help on something ?

 Respond  
Cracker200   -  Aug 08, 2009

Yes Hm.... Looks like it to me But thats just dumb though i mean it's a good snippet but half the people On chat servers have Bad words in there /whois

 Respond  
Atr   -  Aug 08, 2009

So from your description, anyone who has a swear word in their whois can't join any channel you're op in?

 Respond  
Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.