# $Id: cloneflood.tcl, v4.24.b eggdrop-1.6.17 2005/06/29 01:22:47 Exp $

# Begin - Mass Clone Join/Part and Revolving Door Protection v4.24.b
#       Build date: 21st June 2005
#       Last updated: 29th June 2005
#       Copyright © 1999-2005 awyeah (awyeah@awyeah.org)
#       This TCL script is designed to work with eggdrop v1.6.17 or higher

#########################################################################
#      Mass Clone Join/Part and Revolving Door Protection v4.24.b       #
#                                                                       #
#                                                                       #
# Author: awyeah                                         29th June 2005 #
# Email: awyeah@awyeah.org                         Build version 4.24.b #
# Copyright © 2005 awyeah All Rights Reserved    http://www.awyeah.org/ #
#########################################################################
#                                                                       #
# #######                                                               #
# PURPOSE                                                               #
# #######                                                               #
#                                                                       #
# This script basically serves the purpose to prevent mass joining      #
# clones and botnets to join channels. This script detects fast joining #
# parting clones and also detects for mass join/part, fast join/part,   #
# revolving door floods by clones. It also has additional supporting    #
# modules which gives it extra features to eliminate clones. This       #
# script is very efficient and can detect almost any kind of mass join, #
# part and revolving door floods.                                       #
#                                                                       #
# This type of script is most wanted on channels being flooded by bots. #
# I made this script to eliminate floods caused by clones and bots,     #
# since they are a nuisance and hard to deal with. Generally tsunami    #
# and avalanche floods are stopped and controlled by this script since  #
# this script detects clones as they join, so they have no time to      #
# implement a flood since they are detected on join. Tsunami and        #
# avalanche floods as described by the famous sentinel.tcl are rapid    #
# floods originating from single hosts with a variety of numerous       #
# control codes and characters embedded in them.                        #
#                                                                       #
# This script can be run on almost any network on IRC because it does   #
# not utilize any network services.                                     #
#                                                                       #
#########################################################################
#                                                                       #
# ###########                                                           #
# DESCRIPTION                                                           #
# ###########                                                           #
#                                                                       #
# This script will make your eggdrop bot detect mass joining or parting #
# clones and bots where the script is working on and will punish them   #
# immediately and effectively.                                          #
#                                                                       #
# This script also checks for revolving door floods, which are defined  #
# as continuous join/part floods normally containing part messages with #
# a variety of control codes -- another way to flood a channel.         #
#                                                                       #
# The bot owner can select the punishment level for clones. Also kick   #
# messages can be defined and the clone nick kick counter can be        #
# selected to be switched on or off.                                    #
#                                                                       #
# Additionally this script has 3 supporting modules, which are optional #
# to utilize. These three modules consist of detecting fast join/parts, #
# excessive channel clones and suspected mass joining bots and clones.  #
#                                                                       #
#########################################################################
#                                                                       #
# ########                                                              #
# FEATURES                                                              #
# ########                                                              #
#                                                                       #
# (1) This script has three '3' basic types of detection systems:       #
#      (a) Clone Join Detection System                                  #
#      (b) Clone Part Detection System                                  #
#      (c) Clone Join/Part Revolving Door Detection System              #
#                                                                       #
# (2) This script has '2' settings of channels to work on.              #
#      (a) Can be activated to work on user defined channels.           #
#      (b) Can be activated to work on all the channels the bot is on.  #
#                                                                       #
# (3) This script has various options for clone detection such as       #
#     detecting for unresolved idents, matching idents, matching hosts  #
#     or similar characters in nicks.                                   #
#                                                                       #
# (4) This script has separate op, voice, user flag, nick and mask      #
#     exemption options. It also exempt users with unresolved idents.   #
#                                                                       #
# (5) This script has 4 levels of punishment for clones - all of them   #
#     being filter kicks, 1 placing sticky bans, another placing global #
#     bans and the other two general server bans.                       #
#                                                                       #
# (6) You can also select to use the scripts default kick messages when #
#     punishing clones or define your own kick messages and use them.   #
#                                                                       #
# (7) This script has an effective channel locking mechanism, which can #
#     lock channels to specific modes once a clone flood has been       #
#     detected. Also a lock notification notice or message can be sent  #
#     to the channel to notify the users and ops that the channel has   #
#     been locked since clones have been detected.                      #
#                                                                       #
# (8) This script also has a kick counter, which keeps a record of how  #
#     many clones are kicked by the script. The number of clones kicked #
#     are be shown in the end of each kick message if the clone kick    #
#     counter has been enabled.                                         #
#                                                                       #
# (9) Also available are 30 types of banmasks, which can be selected    #
#     with varying wilcards. There are TEN '10' normal banmasks then 10 #
#     separate banmasks for the '?' wildcard and 10 separate banmasks   #
#     for the '*' wildcard. All these banmasks enforce greater range    #
#     for banning clones.                                               #
#                                                                       #
# (10) This script also has different speed queuing options for greater #
#      kicking performance. Speed queues improve scripts effectiveness. #
#      This script has 6 different types of speed queues to select from #
#      so the kicking and banning rate can be selected upon the bot     #
#      owners will so the bot doesn't get Excess Flood or doesn't kick  #
#      out clones very slowly.                                          #
#                                                                       #
# (11) Also this script has 3 precise modules which can be loaded and   #
#      unloaded optionally. These 3 modules provide more enhancements   #
#      when dealing with clones.                                        #
#       (a) Clone Join/Part Scanner (Revolving door)                    #
#       (b) Excessive Clone Scanner (Clones in channel)                 #
#       (c) Suspected Mass Join Botnet Scanner (Botnet join)            #
#                                                                       #
# (12) The EXCESSIVE CLONE SCANNER module is one of most efficient      #
#      scanning system used to eliminate excessive clones residing in   #
#      channels. The scan be made to execute when a user join a channel #
#      or through a timer which is executed continuously after the      #
#      defined number of minutes.                                       #
#                                                                       #
#########################################################################
#                                                                       #
# ############                                                          #
# REQUIREMENTS                                                          #
# ############                                                          #
#                                                                       #
#  The following requirements must be taken into consideration before   #
#  utilizing this script further:                                       #
#                                                                       #
#   (Fields marked with a '*' are compulsory)                           #
#                                                                       #
#    (*1) You must be running EGGDROP version 1.6.17 or higher.         #
#    (*2) You must have TCL v8.3 or higher installed on the system.     #
#     (3) You must have the latest TCL PATCH LEVEL for v8.3 or above.   #
#                                                                       #
#   To FIND the TCL VERSION and PATCH LEVEL your shell is running:      #
#    (1) At your shell prompt type: tclsh                               #
#     (NOTE: Select step 'a' or step 'b' in the next instruction)       #
#        (a) If you have several different versions of tcl installed on #
#            your system, pick the latest version which is installed.   #
#             (i) At shell prompt type: tclsh8.3 (and go to step 2)     #
#        (b) If you have only one version of pick that one or continue  #
#            with 'tclsh' only if it doesn't say to use another name.   #
#    (2) To find your tcl version type: info tclversion                 #
#    (3) To find your tcl patch level type: info patchlevel             #
#    (4) To exit tclsh type: exit                                       #
#                                                                       #
#########################################################################
#                                                                       #
# ############                                                          #
# INSTALLATION                                                          #
# ############                                                          #
#                                                                       #
#  This quick installation tutorial consists of 6 steps. Please follow  #
#  all steps correctly in order to setup your script.                   #
#                                                                       #
#                                                                       #
# (1) UNZIP the zipped file cloneflood.zip and you will get the script  #
#     source file cloneflood.tcl.                                       #
#                                                                       #
#  (a) If you are unzipping the file on MICROSOFT WINDOWS and then want #
#      to upload the TCL file onto your shell you can use 'WINZIP' or   #
#      any other compatible windows unzipping software.                 #
#                                                                       #
#  (b) If you have uploaded the zipped file onto your shell and want to #
#      unzip it. You can use the TAR package available on your shell    #
#      to unzip the script file:                                        #
#                                                                       #
#               tar zxvf cloneflood.tar.gz                              #
#                                                                       #
#    (Note: This file is not gziped for UNIX operating systems so only  #
#     WINZIP on Microsoft Windows can be used to unzip this file)       #
#                                                                       #
#                                                                       #
# (2) PLACE the unzipped file cloneflood.tcl file in your eggdrops      #
#     '/scripts' folder along with your other TCL scripts.              #
#     (Note: You have to upload the script file to your shell if you    #
#     have unzipped it on your hard drive)                              #
#                                                                       #
# (3) OPEN your eggdrops configuration (.conf) file and add a link at   #
#     the bottom of the configuration file to the path of clone flood   #
#     remover script, it would be:                                      #
#                                                                       #
#               source scripts/cloneflood.tcl                           #
#                                                                       #
#                                                                       #
# (4) SAVE your bots configuration file.                                #
#                                                                       #
# (5) OPEN cloneflood.tcl and start configuring script variables.       #
#     (START FROM: 'Start configuring variables from here!'             #
#     END AT: 'Congratulations! Script configuration is now complete')  #
#                                                                       #
# (6) RESTART your eggdrop and your done!                               #
#                                                                       #
#########################################################################
#                                                                       #
# ########                                                              #
# VERSIONS                                                              #
# ########                                                              #
#                                                                       #
#  v4.24.b  - Fixed a chanset bug encoutered for setting specific       #
# (29/06/05)  channel join flood settings on rehash and restart.        #
#           - Added a new bind on init-server and rehash to a timely    #
#             delayed procedure for it to change specific channel       #
#             settings for flood-join and to check if the bot is on the #
#             channels defined for it before the settings are modified. #
#           - Added a control code stripping filter clone:strip:codes   #
#             for filtering control codes. If the eggdrop version is    #
#             less than 1.6.17 use a string map and regsub filter and   #
#             if the eggdrop version is greater than or equal to 1.6.17 #
#             then utilize the 'stripcodes' function.                   #
#           - Added more descriptive comments in the settings example   #
#             and explanation fields.                                   #
#                                                                       #
#  v4.18.b  - Initial release of script.                                #
# (21/06/05)                                                            #
#                                                                       #
#########################################################################
#                                                                       #
# ########                                                              #
# CONTACTS                                                              #
# ########                                                              #
#                                                                       #
#  (*) For any suggestions, comments, questions or bugs reports feel    #
#      free to email me at:                                             #
#                                                                       #
#               awyeah@awyeah.org                                       #
#                                                                       #
#                                                                       #
#  (*) You can also contact me on MSN Messenger. My MSN ID is:          #
#                                                                       #
#               awyeah@awyeah.org                                       #
#                                                                       #
#                                                                       #
#  (*) You can also catch me on The DALnet Network:                     #
#                                                                       #
#               /server irc.dal.net:6667, Nick: awyeah                  #
#                      Channels: #eggdrops, #awyeah, #dalusers, #eleet  #
#                                                                       #
#  (*) Also I am mostly available on my own IRC Network:                #
#                                                                       #
#               /server irc.awyeah.org:6667, Nick: awyeah               #
#                                                                       #
#                                                                       #
# Note: I have a lot of people bothering me for scripts which they      #
#       HAVEN'T configured properly themselves. So please read ALL the  #
#       comments provided while configuring the variables and make sure #
#       you follow the same standard format as given in examples when   #
#       setting each variable in the script.                            #
#                                                                       #
#########################################################################
#                                                                       #
# #########                                                             #
# COPYRIGHT                                                             #
# #########                                                             #
#                                                                       #
# This program is a free software; you can distribute it under the      #
# terms of the GNU General Public License under Section 1 as published  #
# by the Free Software Foundation; either version 2 of the license, or  #
# (at your option) any later version.                                   #
#                                                                       #
# This program is distributed in the hope that it will be useful,       #
# but WITHOUT ANY WARRANTY; without even the implied warranty of        #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
# GNU General Public License for more details.                          #
#                                                                       #
# WARNING:                                                              #
# This program is protected by copyright law and international          #
# treaties. Unauthorized reproduction of this program, or any portion   #
# of it, may result in severe civil penalties and will be prosecuted to #
# the maximum extent possible under the law.                            #
#                                                                       #
#########################################################################
#                                                                       #
# #########                                                             #
# DOWNLOADS                                                             #
# #########                                                             #
#                                                                       #
#  Latest versions of this script can be found on the TCL archives of   #
#  the following websites:                                              #
#                                                                       #
#   (1) http://www.awyeah.org/                                          #
#   (2) http://www.egghelp.org/                                         #
#   (3) http://www.tclscript.com/                                       #
#                                                                       #
#########################################################################
#                                                                       #
# #######                                                               #
# CREDITS                                                               #
# #######                                                               #
#                                                                       #
#  (*) I would like to thank 'Sir_Fz' aka Opposing who helped me with   #
#      this script by giving me ideas on how to improve the coding,     #
#      making the script execute faster and be more efficient.          #
#                                                                       #
#  (*) I give credit to 'user' for the improved banmask selector used   #
#      in the script with 30 types of banmasks to choose from.          #
#                                                                       #
#  (*) Also I would like to thank 'demond' for helping me with a last   #
#      minute easier and effective solution for removing duplicate      #
#      elements found in a list for the join and part flood protection. #
#                                                                       #
#  (*) Special thanks to 'LoVeGirL' who did the beta testing for this   #
#      script and reported any pre-release bugs and errors encountered. #
#                                                                       #
#                                                                       #
#  ./awyeah                                                             #
#                                                                       #
#########################################################################


##############################################
### Start configuring variables from here! ###
##############################################

#--------------------------------------#
#    SET CHANNEL ACTIVATION OPTIONS    #
#--------------------------------------#

#Set the type of channels you would like this script to work on.
#USAGE: [1/2] (1=USER DEFINED CHANNELS, 2=ALL CHANNELS THE BOT IS ON)
#Use '1' for 'user defined channels' (Will detect clone floods only on the channels defined for the script)
#Use '2' for 'all the channels' the bot is on. (Will detect clone floods on every channel the bot is opped on)
set clone_channel_type "2"

###USE THIS ONLY IF YOU HAVE SET THE PREVIOUS SETTING TO '1'###
#Set this if you have set the previous setting to '1' for user defined channels, if not then you do
#not need to configure this option. Set the channels here on which this script would work.
#USAGE: set clone_channels "#channel1 #channel2 #channel3" (separate each element with a space)
set clone_channels "#BlueCat"


#------------------------------------#
#    SET CLONE DETECTION SETTINGS    #
#------------------------------------#

#NOTE: Settings in this section of the script are very important and should be configured properly,
#for the script to work efficiently.

### EXPLANATION OF MECHANISM ###
#Settings listed below the define the 'number' of clone joins, parts in less than
#a definite interval of 'seconds'. (Please read these carefully before script setup)
#######################################################################################
##[1] A value for JOIN FLOOD '3:5' can be read as:                                   ##
## - '3' or more joins by a single host in less than or equal to '5' seconds.        ##
#######################################################################################
##[2] A value for REVOLVIND DOOR FLOOD '2:3' can be read as:                         ##
## - '2' or more join/parts by a single host in less than or equal to '3' seconds.   ##
#######################################################################################


### RECOMMENDED SETTINGS ###
#Please use the recommended settings or settings similar to the recommended
#ones for optimal results. (If you don't understand leave them as they are)
#FORMAT: <how_many_[joins|parts|revolving_doors]>:<how_many_seconds>
################################################################################
##[1] Values recommended for JOIN FLOOD are:                                  ##
## a) 2:2    b) 2:3    c) 3:3    d) 3:4    e) 4:3   f) 4:5   g) 5:3    h) 5:4 ##
################################################################################
##[2] Values recommended for PART FLOOD are:                                  ##
## a) 2:1    b) 2:2    c) 2:3     d) 3:2   e) 3:3   f) 4:2   g) 4:3    h) 5:3 ##
################################################################################
##[3] Values recommended for REVOLVING DOOR FLOOD are:                        ##
## a) 2:2    b) 2:3    c) 2:4    d) 3:2    e) 3:4   f) 4:3   g) 4:4    h) 4:5 ##
################################################################################
#Any 'number' and 'time limit' setting under '5' seconds would be good enough.


### CLONE JOIN FLOOD SETTING ###
#Set how many 'JOINS' from a single *!*@host in how many 'SECONDS' would trigger a clone JOIN FLOOD.
#If you want to DISABLE join flood detection, then set this value as '0' or '0:0'.
#(Please use recommended or similar to recommended values for best performance)
#USAGE: set clone_join_flood "number_of_joins:number_of_seconds"
set clone_join_flood "3:2"

### CLONE PART FLOOD SETTING ###
#Set how many 'PARTS' from a single *!*@host in how many 'SECONDS' would trigger a clone PART FLOOD.
#If you want to DISABLE part flood detection, then set this value as '0' or '0:0'.
#(Please use recommended or similar to recommended values for best performance)
#USAGE: set clone_part_flood "number_of_parts:number_of_seconds"
set clone_part_flood "3:2"

### CLONE REVOLDING DOOR (JOIN/PART) FLOOD SETTING ###
#Set how  many clone 'JOIN/PARTS' from a single *!*@host in how many 'SECONDS' would trigger a clone
#REVOLVING DOOR FLOOD. (Please use recommended or similar to recommended values for best performance)
#If you want to DISABLE join/part flood detection, then set this value as '0' or '0:0'.
#USAGE: set clone_joinpart_flood "number_of_joins/parts:number_of_seconds"
set clone_joinpart_flood "3:2"


#-----------------------------------------#
#    SET CLONE IDENT DETECION SETTINGS    #
#-----------------------------------------#

#Set this if you only want to detect clones with similar a *!*host who have UNRESOLVED IDENTS (idents
#with a '~' at the front). Mostly clones are loaded from open port proxies whose idents are not
#resolved by IRC servers and are given an asterisk '~' in the front so they can be detected easily.
#USAGE: [0/1] (0=DON'T CARE ABOUT IDENTS, 1=ONLY DETECT CLONES WITH UNRESOLVED IDENTS)
#Use '0' if you want to make the bot check ALL types of idents.
#Use '1' if you ONLY want the bot to check UNRESOLVED idents (idents with a '~' sign)
#(Note: DISABLING this would be a good option so the bot can catch all types of clones)
set clone_unresolved_ident "0"


#-----------------------------------------#
#    SET CLONE FLOOD MODELOCK SETTINGS    #
#-----------------------------------------#

#Set this if you want to ENABLE 'channel mode locking' when a clone join, part or revolving door
#flood occurs. This mode lock enforcement can prevent text/notice, avalanche and tsunami floods
#from botnets and clones joining in fast.
#Usage: [0/1] (0=OFF, 1=ON)
set clone_modelock_switch "1"

### IMPORTANT ###
#All the variables below for 'mode lock settings block' should ONLY be set if channel mode locking
#has been enabled, i.e. clone_modelock_switch has been set to '1', otherwise please ignore all
#the mode lock settings block.

#Set this to the 'PRIMARY MODE LOCK' you wish to lock on the channel. Also remember when the bot
#is unlocking the channel this modelock will be checked, if it is present then only it will be
#unlocked, otherwise since other ops might have removed it already the bot wouldn't unlock it then.
#Important: Only '1' mode lock can be used for this setting - HIGH PRIORITY MODELOCK
#USAGE: 'm', 'M', 'R' etc. (Please do not use +l or +k)
set clone_primary_modelock "mr"

#Set this to the 'SECONDARY MODE LOCK' you wish to lock on the channel besides the primary modelock.
#This is an additional mode lock supporting the primary modelock, if you wish to place additional
#modelocks on the channel. This is an OPTIONAL setting, if you do not wish to place any additional
#modelock besides the primary modelock then please leave this setting to by empty as "".
#Important: More than '1' modelocks can also be used for this setting.
#USAGE: 'MR', 'Rc', 'iRM' etc. (The +l or +k modelocks can be used)
set clone_secondary_modelock "R"

#Set the time in SECONDS after which the modelock will be removed -- only if the primary modelock
#is found locked on the channel and has not been already removed by other channel ops yet.
#USAGE: A value in between '3 - 10' seconds should be good enough.
set clone_modelock_time "15"


#-----------------------------------------------------#
#    SET CLONE FLOOD MODELOCK NOTIFICATION OPTIONS    #
#-----------------------------------------------------#

### IMPORTANT ###
#All these variables should ONLY be set if you have chosen to lock the channel on a clone flood,
#i.e. clone_modelock_switch has been set to '1', otherwise please ignore all the 'mode lock
#notification setting block'.

#Set this if you want to NOTIFY channel users when the channel has been locked due a clone flood
#in the channel. This notification will be sent by the type of sending option selected -- next setting.
#USAGE: [0/1] - (0=NO NOTIFICATION, 1=SEND NOTIFICATION)
set clone_notify_switch "0"

#Set the type of message sending option to the channel to use when sending the channel modelock
#notification message. There are 3 different sending options available to choose from.
#USAGE: [1/2/3] - (1=CHANNEL TEXT, 2=CHANNEL NOTICE, 3=CHANNEL OP-NOTICE)
#(Note: The default option for this setting is: '2')
set clone_notify_message_type "2"

#Set the TYPE of NOTIFICATION MESSAGE to use here. If you wish to use the scripts default
#notice message with its content or your own user-defined notification message.
#USAGE: [1/2] - (1=SCRIPTS DEFAULT MESSAGE, 2=USER DEFINFED MESSAGE)
#(If you have selected option '2' please set your message below)
set clone_notify_type "1"

###USE THIS ONLY IF YOU HAVE SET THE PREVIOUS SETTING TO '2'###
#Set the modelock notification MESSAGE content here, which will be sent to the channel when the
#channel is locked. (If you do not specify a notice then the scripts default message will be used)
set clone_notify_message "CLONE FLOOD: Channel has been locked to +$clone_primary_modelock$clone_secondary_modelock for $clone_modelock_time seconds."


#-----------------------------------#
#    SET CLONE PUNISHMENT METHOD    #
#-----------------------------------#

#Set the punishment level of removing the clones.
#USAGE: [1/2/3/4] - [1=KICK, 2=KICK/BAN, 3=STICKY KICK/BAN, 4=GLOBAL KICK/BAN]
#Use '1' to 'kick' all clones from the channel. (Minimal punishment)
#Use '2' to 'kick and ban' all clones from the channel. (Normal punishment)
#Use '3' to 'kick and ban' all clones using a STICKY ban from the bots banning mechanism. (Moderate punishment)
#Use '4' to 'kick and ban' all clones using a GLOBAL ban from all channels where users with clone *!*@host's are found on with the bot. (Extreme punishment)
#(NOTE: Options '3' and '4' will ignore all on op, voice, flag, nick and mask exemption options set)
set clone_punishment_type "2"


#-----------------------------------------#
#    SET CLONE PUNISHMENT BANMASK TYPE    #
#-----------------------------------------#

#Select the type of banmask (from the list below) to use when banning clones.
#-----------------------------------------------------------------------------
#RECOMMENDED SETTINGS FOR BANMASK SELECTION:
# - It is recommend that you use setting number '2'
# - If you want a greater range you can use settings number '12' or '22'
# - Else other options with large ranges are settings number '4', '14' or '24'
#-----------------------------------------------------------------------------
# Valid types are:
#   0 - *!user@host.domain
#   1 - *!*user@host.domain
#   2 - *!*@host.domain
#   3 - *!*user@*.domain
#   4 - *!*@*.domain
#   5 - nick!user@host.domain
#   6 - nick!*user@host.domain
#   7 - nick!*@host.domain
#   8 - nick!*user@*.domain
#   9 - nick!*@*.domain
#  You can also specify 2 types of wild carded banmasks, 10-19 and 20-29 which correspond
#  to the types 0-9 using this sub-rule.
#
#  For the 10-19 range, if the user's host.domain is a [name/ip]:
#    name - It will replace the numbers in the host.domain with a '?' (question mark) wildcard.
#    ip   - It will mask as normal, with no '?' replacements as does hostname.
#
#  For the 20-29 range, if the user's host.domain is a [name/ip]:
#    name - It will replace the numbers in the host.domain with a '*' (star) wildcard.
#    ip   - It will mask as normal, with no '*' replacements as does hostname.
#
#  Explanations of the "?" and "*" wildcards:
#   - The character '?' matches any single character
#   - The character '*' matches 0 or more characters of any type
#
#   * A character can be an alphabet, a number or any other symbol on the keyboard.
#-----------------------------------------------------------------------------------
#  Example:
#   [Mask #2] awyeah!god@125-628-dialup.mindspring.com => *!*@125-628-dialup.mindspring.com
#   [Mask #12] awyeah!god@125-628-dialup.mindspring.com => *!*@???-???-dialup.mindspring.com
#   [Mask #22] awyeah!god@125-628-dialup.mindspring.com => *!*@*-*-dialup.mindspring.com
set clone_banmask_type "12"

#Set the amount of time in MINUTES to ban the clones for. This will be the time in minutes the
#clones will remain banned from the channel and then removed if the ban is not set to be permanent.
#(NOTE: If you want the ban to be PERMANENT, set this value as '0')
#USAGE: Any positive integer or number
set clone_bantime "18"


#-----------------------------------#
#    SET CLONE EXEMPTION OPTIONS    #
#-----------------------------------#

### IMPORTANT - PLEASE READ THIS ###
#When channel OP/VOICE exemption is enabled only current channel OPS/VOICES residing in the channel
#would be exempted, since new joining-in ops/voices will take a second or so to get opped/voiced through
#ChanServ or any other IRC service running on your network. All these exemption settings will be valid
#if the clone punishment type is set to '1' or '2'. This is because 'sticky' and 'global' bans are bot's
#internal banning mechanisms and the bot doesn't exempt ops/voices with its internal banning mechanism.

#### EXEMPT CHANNEL OPERATORS FROM CLONE PUNISHMENTS ####
#Set this if you want the bot to exempt all currently residing OPS even though their *!*@host matches
#that of the clones banned. (This is a useful setting for not punishing 'OPS' matching clone hosts)
#USAGE: [0/1] - (0=WILL NOT EXEMPT OPS, 1=WILL EXEMPT OPS)
#If set to '0' channel OPS *will be kicked* if their *!*@host matches that of the clones.
#If set to '1' channel OPS *will NOT be kicked* even if their *!*@host matches that of the clones.
set clone_exempt_ops "1"

#### EXEMPT CHANNEL VOICES FROM CLONE PUNISHMENTS ####
#Set this if you want the bot to exempt all current residing VOICES even though their *!*@host matches
#that of the clones banned. (This is a useful setting for not punishing 'VOICES' matching clone hosts)
#USAGE: [0/1] - (0=WILL NOT EXEMPT VOICES, 1=WILL EXEMPT VOICES)
#If set to '0' channel VOICES *will be kicked* if their *!*@host matches that of the clones.
#If set to '1' channel VOICES *will NOT be kicked* even if their *!*@host matches that of the clones.
set clone_exempt_voices "1"

#### EXEMPT SPECIFIC USER FLAGS FROM CLONE PUNISHMENTS ####
#Set this if you have enabled to exempt user's with certain 'user flags' on the bots userlist even
#if their *!*@host matches that of the clones banned. Set the 'flags' for bot owners, masters, ops,
#voices, friends etc which will be exempted from clone punishments.
#USAGE: "mnf|mnf", "mn|o", "ovbf|-" etc. ("-" means no flags)
#(Note: If you want to exempt no flags set this to '-|-')
#FORMAT: "GLOBAL_FLAGS|CHANNEL_FLAGS"
set clone_exempt_flags "mn|fbov"

#### EXEMPT SPECIFIC NICKS FROM CLONE PUNISHMENTS ####
#Set the list of nicks here you want the script to exempt even if their *!*@host matches that of the
#clones banned. If any of these nicks are detected as clones then they will NOT be punished by the
#clone detector. This is a good option for exempting certain nicks, characters or specific
#keywords in nicks which you don't want the clone nick to punish.
#(NOTE: Wildcards such as '*' and '?' can be used for each entry within the list)
#Guidelines for ADDING and MODIFYING the nick exemption list:
#-------------------------------------------------------------------------------
#1) Please make sure every nick pattern is enclosed within " " quotation marks.
#2) Please make sure each nick pattern is separated by a SPACE or is placed in a NEW LINE.
#3) Please add suitable nick patterns, which effect small patterns, ranges, if you use wildcards.
#4) You can add an entire nick, part of a nick or specific word of a nick to exempt.
#5) You can also add a nick, with the '?' wildcard in it also or the '*' wildcard surrounding the nick.
#-------------------------------------------------------------------------------------------------------
#Explanation of the wildcards masks:
# - The character '*' matches 0 or more characters of any type
# - The character '?' matches any single character
#-----------------------------------------------------------------
#If you do not have any nick to exempt: set clone_exempt_nicks ""
set clone_exempt_nicks {
"nick1"
"nick2"
"nick3"
}

#### EXEMPT SPECIFIC MASKS FROM CLONE PUNISHMENTS ####
#Set the list of masks here you want the script to exempt even if their *!*@host matches that of the
#clones banned. If any of these masks are detected as clones then they will NOT be punished by the
#clone detector. This is a good option for exempting certain masks, characters or specific
#keywords in user masks which you don't want the clone detector to punish.
#(NOTE: Wildcards such as '*' and '?' can be used for each entry within the list)
#Guidelines for ADDING and MODIFYING the mask exemption list:
#----------------------------------------------------------------------------------
#1) Please remember all masks should are in the format: nick!ident@host.domain.com
#2) Please make sure every mask pattern is enclosed within " " quotation marks.
#3) Please make sure each mask pattern is separated by a SPACE or is placed in a NEW LINE.
#4) Please add suitable mask patterns, which effect small patterns, ranges, if you use wildcards.
#5) You can add an entire mask 'nick!user@host.domain' or a part of a mask to exempt.
#6) You can also add a mask, with the '?' wildcard in it also or the '*' wildcard surrounding the mask.
#-------------------------------------------------------------------------------------------------------
#Explanation of the wildcards masks:
# - The character '*' matches 0 or more characters of any type
# - The character '?' matches any single character
#-----------------------------------------------------------------
#If you do not have any nick to exempt: set clone_exempt_masks ""
set clone_exempt_masks {
"nick!ident@host.domain.com"
"*!*@host.domain.com"
"*!*@*.domain.com"
}


#----------------------------------------#
#    SET CLONE PUNIHSMENT SPEED QUEUE    #
#----------------------------------------#

###NOTE: ONLY SET this block if PUNISHMENT TYPE has been SET to '1' or '2'###

#Select the type of speed queue to use for kicking and banning the clones. This option has been
#given because the bots banning and kicking mechanism has separate queues so it doesn't get flooded
#of the server (Excess flood), since some queues are delayed and some are fast. There are 7 types of
#queues available to select from depending upon the speed/rate you wish to remove the clones at.
#------------------------------------------------------------------------------------------------
#SPEED: The speed of listed queues is from '1 - 7' (given in the order: slowest ---> fastest)
#USAGE: [1/2/3/4/6/7] (There are 7 types of queues available to choose from)
#1 - puthelp  (BAN: 1-2 second delayed queue, KICK: 1-2 second delayed queue)
#2 - pushmode  (BAN: Default speed queue, KICK: 1 combines number channel modes into one line as possible)
#3 - putkick  (BAN: Default speed queue, KICK: combines number of kicks into one line as possible)
#4 - putserv  (BAN: Default speed queue, KICK: Default speed queue)
#5 - putquick  (BAN: Fast speed queue, KICK: Fast speed queue)
#6 - putdccraw  (BAN: Fastest speed without queuing, KICK: Fastest speed without queuing)
#(WARNING: Speed number 7 uses no queues, so it can flood your bot off the server at most times)
#If you are not sure which speed queue to use, use the default speed queue i.e., speed number '4'.
set clone_kick_queue "5"

###USE THIS 'ONLY' IF YOU HAVE SELECTED THE SPEED QUEUE OPTION TO BE '1', '2', '4' OR '5'###
#Set this if you have only selected one of the 'puthelp', 'putserv' or 'putquick' speed queues. This
#option will add a '-next' option in front of the speed queues making execution more faster. The '-next'
#switch option pushes messages to the front of the queue so they are executed as faster as possible.
#USAGE: [0/1] (0=DISABLE THE '-NEXT' OPTION, 1=ENABLE THE '-NEXT' OPTION)
#Use '0' if you DO NOT wish to use the '-next' option in your speed queues.
#Use '1' if you WISH to use the '-next' option in your speed queues for faster performance.
#(NOTE: If you DO NOT use the '-next' option with the 1, 2, 4 or 5 speed queues the speed of placing
#bans and the speed of kicking users can slow down a bit)
set clone_queue_next_option "1"


#-------------------------------------#
#    SETUP CLONE KICK MESSAGE TYPE    #
#-------------------------------------#

#Set this if you would like to use your OWN kick messages for kicking clones or wish to use the
#DEFAULT script kick messages for kicking clones. (The scripts default kick message is more informative)
#(NOTE: If you want to define your own kick messages -- you will have to define 6 separate ones)
#USAGE: [0/1] (O=SCRIPT DEFAULT KICK MESSAGES, 1=USER DEFINED KICK MESSAGES)
set clone_kick_msg_type "0"

###USE THIS ONLY IF YOU HAVE SET THE PREVIOUS SETTING TO '1'###
#Set these to your customizable user defined KICK messages. These kick messages will be shown when clone
#are kicked out by the bot. These kick messages can contain all types of control codes, which are
#supported on IRC such as colors, bolds, underlines, reverses and etc.
#(NOTE: Listed below are '6' different types of kick messages for the clone detector)
###################################################################################################################################
##1) Set the kick message for MASS JOINING CLONES
set clone_kick_user1_msg "0,1 Clone Join Flood 12,0 - Mass 2joining clones 12detected from 2host"
###################################################################################################################################
##2) Set the kick message for MASS PARTING CLONES
set clone_kick_user2_msg "0,1 Clone Part Flood 12,0 - Mass 2parting clones 12detected from 2host"
###################################################################################################################################
##3) Set the kick message for MASS JOINING/PARTING CLONES, REVOLVING DOOR FLOOD
set clone_kick_user3_msg "0,1 Clone Join/Part Flood 12,0 - Mass 2join/part revolving door 12detected from 2host"
###################################################################################################################################
##4) Set the kick message for FAST JOINING/PARTING CLONES
set clone_kick_user4_msg "0,1 Clone Join/Part Flood 12,0 - 12Fast 2join/part flood 12detected from 2host"
###################################################################################################################################
##5) Set the kick message for EXCESSIVE RESIDING CLONES IN A CHANNEL
set clone_kick_user5_msg "0,1 Excessive Clones 12,0 - Excessive 2clones residing 12in channel 12detected from 2host"
###################################################################################################################################
##6) Set the kick message for SUSPECTED MASS JOINING CLONE BOTNET
set clone_kick_user6_msg "0,1 Suspected Mass Joining Clones 12,0 - 12Suspected 2ordered nick 12botnet 12detected from 2host"
###################################################################################################################################
#(HINT: The smaller the kick message and the less control codes the kick message contains the more faster kicks will be placed)


#------------------------------#
#    SET CLONE KICK COUNTER    #
#------------------------------#

#Set this if you wish to use a kick counter in every clone kick message. The number of kicks done
#by the clone detector will be displayed at the end of each clone kick message.
#(NOTE: The clone kick counter can slow down script's kicking performance)
#USAGE: [0/1] (0=DISABLED, 1=ENABLED)
set clone_kick_counter_type "1"


###USE THIS ONLY IF YOU HAVE SET THE PREVIOUS SETTING TO '1'###
#Set the FILENAME and PATH here in which you want to store the clone nicks KICK COUNTING DATABASE in.
#If you do not know which path to specify just type in a filename for your clone nick kick counting database.
#CHECKING CURRENT PATH: To see your current directory path type 'pwd' at your shell prompt
#(Note: The filename and extension [*.file] of the file can be named as anything)
#Example: set clone_kick_counter_file "/usr/home/awyeah/eggdrop/clonekicks.dat"
set clone_kick_counter_file "clonekicks.dat"


############################
##### OPTIONAL MODULES #####
############################

#This script contains 3 additional modules, which can be turned ON and OFF depending upon the desired
#will of the bot owner. The 3 modules are listed below as follows:
######################################################
#[1] CLONE FAST JOIN/PART SCANNER (REVOLVING DOOR)
#[2] EXCESSIVE CLONE SCANNER (CLONES IN CHANNEL)
#[3] SUSPECTED MASS JOIN BOTNET SCANNER (BOTNET JOIN)
######################################################
#(NOTE: As default all modules are set OFF)


############################################################
#### (1) ADDITIONAL MODULE: CLONE FAST JOIN/PART SCANNER ###
############################################################

#Set this if you want to enable punishing users who join and then quickly part a channel. This module
#punishes users who do fast join/parts and revolving doors. This is a good protection against multiple
#join/parts from a single host to eliminate bots who flood through part messages.
#USAGE: [0/1] (0=DISABLED, 1=ENABLED)
set clone_flyby_type "0"

### IMPORTANT ###
#All the variables below for 'clone fast join/part scanner module' should ONLY be set if clone fast
#join/part scanner module has been enabled, i.e. clone_flyby_type has been set to '1', otherwise please
#ignore all the mode lock settings block.

#Set the time in SECONDS for which the clone fast join/part scanner module will check for fast joins
#and parts. If a join and part is done within this time then it would be counted as a revolving door
#otherwise it will be detected as a normal join and part.
#USAGE: Any positive integer (Recommended: 2 - 5 secs)
set clone_flyby_time "2"

#Set this if you only want to check for fast joins/parts which only contain a part message and have
#joined and parted within the defined number of seconds which is defined above. Set the length of
#the part message here. If the part message is less than this then it will not be counted as
#as a fast join and part, but if it is equal or greater to this number then it will be detected.
#TURN OFF: To turn off this setting, set the value as '0'
#USAGE: Any positive integer or number
set clone_flyby_reason_length "0"


######################################################
### (2) ADDITIONAL MODULE: EXCESSIVE CLONE SCANNER ###
######################################################

#Set this if you want the bot to check the channel for clones. Clones will be detect based upon
#similar *!*@hosts of users residing in the channel. This module can slow the bot down a bit based
#upon which type of excessive clone checking is used.
#USAGE: [0/1/2/3] - (0=OFF, 1=CHECK ON-JOIN, 2=FULL SCAN ON-JOIN, 3=FULL SCAN EVERY 'X' MINUTES)
#Use '0' if you want to DISABLE checking the channel for clones. (DISABLE clone scanner module)
#Use '1' if you want to scan a channel for clones for the user who joined in.
#Use '2' if you want to fully scan a channel for all clones when a user joins in.
#Use '3' if you want to fully scan a channel for all clones when a user joins in every 'x' minutes.
#(Note: Setting '1' and '2' can be slow the bot down if the channel has a lot of joins and parts)
set clone_remover_type "0"

###SET THIS ONLY IF THE ABOVE SETTING HAS BEEN SET TO '3'###
#Set this to the time in MINUTES, which will be executed continuously for a scan of all the
#clones in the channel. (NOTE: Please do not set this value very low as it can make the bot slow
#if the channel has a lot of users residing)
#USAGE: Any positive integer or number
set clone_remover_time "10"

#Set the maximum number of clones you would like to allow in a channel. If the limit of clones
#exceeds this number then all the users with matching similar *!*@host's i.e. clones residing in
#the channel would be removed from the channel.
#USAGE: Any positive number greater than 1
set clone_max_allowed "2"


#################################################################
### (3) ADDITIONAL MODULE: SUSPECTED MASS JOIN BOTNET SCANNER ###
#################################################################

###NOTE: THIS IS AN ADVANCED SETTING - IF YOU DON'T UNDERSTAND THEN DON'T ENABLE THIS###
#Set this if you want to detect suspected mass joining botnet nicks. Suspected mass joining botnet
#nicks will be detected through regular expression matching patterns defined in the list given below.
#USAGE: [0/1/2] (0=OFF, 1=DETECT ALL TYPES OF IDENTS, 2=ONLY DETECT IDENTS WITH ASTERISKS '~')
#Use '0' if you want to DISABLE checking the channel suspecting mass joining botnets.
#Use '1' if you want to scan all idents for suspecting mass joining botnets.
#Use '2' if you want to scan all unresolved idents (idents with '~') for suspecting mass joining botnets.
#(Recommended: Setting '2' is recommended to be used)
set clone_regexp_match_switch "0"

#Set the maximum number of mass joining botnet clones you would like to allow to join in a channel.
#If the number of clones exceeds this limit then all the detected patterns of nicks will be punished
#from the channel. (Recommended: The recommended setting is '1')
#USAGE: Any positive number
set clone_regexp_clones "1"

#Set the list of REGULAR EXPRESSION matching patterns that will detect randomly generated, drone,
#botnet nicks which will be detected for mass joining clones. IF YOU DO NOT UNDERSTAND HOW TO EDIT
#OR ADD NEW REGULAR EXPRESSION PATTERNS THEN PLEASE LEAVE THIS SETTING ALONE.
set clone_regexp_matches {
{ [a-z]{1}[0-9]{1}[a-z]{1}[0-9]{1}[a-z]{1} }
{ [a-z]{2}[0-9]{2}[a-z]{2}[0-9]{2} }
{ [a-z]{1}[0-9]{4,8}[a-z]{1} }
{ [A-Z]{1}[0-9]{4,8}[A-Z]{1} }
{ [a-z]{2,8}-[0-9]{3,6} }
{ [a-z]{2,8}_[0-9]{3,6} }
{ [a-z]{2,8}[0-9]{3,6} }
{ [a-z]{4,}[0-9]{4,5} }
{ \[[a-z]{4,}\]-{3,} }
{ \[[a-z]{4,}\]_{3,} }
{ \[[a-z]{4,}\]{3,} }
{ \[[A-Z]{4,}\]{3,} }
}

#############################################################
### Congratulations! Script configuration is now complete ###
#############################################################


##############################################################################
### Don't edit anything else from this point onwards even if you know tcl! ###
##############################################################################

bind join - "*" clone:join:chan:type
bind part - "*" clone:part:chan:type
bind join - "*" clone:remover:module
bind flud - join clone:join:flood:check
bind evnt - rehash clone:delay:settings
bind evnt - prerehash clone:clear:utimers
bind evnt - init-server clone:delay:settings
if {$clone_kick_counter_type == 1} { bind kick - "*" clone:kick:counter }

if {($clone_remover_type == 3) && ![string match "*clone:remover:scan*" [timers]]} { timer $clone_remover_time clone:remover:scan }
if {($clone_exempt_ops == 0) && ($clone_exempt_voices == 0)} { set clone_exempt_ops_voices "1"
} elseif {($clone_exempt_ops == 0) && ($clone_exempt_voices == 1)} { set clone_exempt_ops_voices "2"
} elseif {($clone_exempt_ops == 1) && ($clone_exempt_voices == 0)} { set clone_exempt_ops_voices "3"
} elseif {($clone_exempt_ops == 1) && ($clone_exempt_voices == 1)} { set clone_exempt_ops_voices "4"
} else { set clone_exempt_ops_voices "1" }


proc clone:delay:settings {type} {
 timer 3 [list clone:modify:settings $type]
}

proc clone:modify:settings {type} {
 global clone_channel_type clone_channels clone_join_flood
  switch -- $clone_channel_type {
   1 { set clone_working_channels [split $clone_channels] }
   2 { set clone_working_channels [channels] }
   default { set clone_working_channels [channels] }
  }
  foreach clone_channel $clone_working_channels {
   if {[botonchan $clone_channel]} {
    channel set $clone_channel flood-join $clone_join_flood
    }
  }
 putlog "\002MASS JOIN/PART CLONE PROTECTION\002: Configuring channel flood-join settings."
 save
}

proc clone:join:chan:type {nick uhost hand chan} {
 global clone_channel_type clone_channels
  if {![isbotnick $nick] && [botisop $chan]} {
   switch -- $clone_channel_type {
    1 {
        if {[lsearch -exact [split [string tolower $clone_channels]] [string tolower $chan]] != -1} {
         clone:join:check $nick $uhost $hand $chan
         }
    }
    2 {
        if {[string equal "#" [string index $chan 0]]} {
         clone:join:check $nick $uhost $hand $chan
         }
    }
    default { putlog "\002MASS JOIN/PART CLONE PROTECTION\002: No channel type selected."; return 0 }
    }
  }
}

proc clone:part:chan:type {nick uhost hand chan {reason ""}} {
 global clone_channel_type clone_channels
  if {![isbotnick $nick] && [botisop $chan]} {
   switch -- $clone_channel_type {
    1 {
        if {[lsearch -exact [split [string tolower $clone_channels]] [string tolower $chan]] != -1} {
         clone:part:check $nick $uhost $hand $chan $reason
         }
    }
    2 {
        if {[string equal "#" [string index $chan 0]]} {
         clone:part:check $nick $uhost $hand $chan $reason
         }
    }
    default { putlog "\002MASS JOIN/PART CLONE PROTECTION\002: No channel type selected."; return 0 }
    }
  }
}


proc clone:join:check {nick uhost hand chan} {
 global clone_exempt_nicks clone_unresolved_ident clone_banmask_type clone_nicks_joinpart clone_regexp_clones
 global clone_nicks_join clone_modelock_switch clone_exempt_masks clone_bantime clone_regexp_match_switch
 global clone_join_flood clone_joinpart_flood clone_type1_count clone_type3_count clone_regexp_matches
 global clone_exempt_flags clone_flyby_type clone_flyby clone_flyby_time clone_punishment_type
 set nickuhost [string tolower $nick:$uhost]; set hostchan [lindex [split [string tolower $uhost] @] 1]:[string tolower $chan]
 if {($clone_unresolved_ident == 1) && ![string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { return 0 }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 0]]]} { foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] { if {[matchattr $hand $clone_exempt_global_flag]} { return 0 } } }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 1]]]} { foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] { if {[matchattr $hand $clone_exempt_local_flag $chan]} { return 0 } } }
  if {$clone_exempt_nicks != ""} { foreach clone_exempt_nick $clone_exempt_nicks { if {[string match -nocase $clone_exempt_nick $nick]} { return 0 } } }
  if {$clone_exempt_masks != ""} { foreach clone_exempt_mask $clone_exempt_masks { if {[string match -nocase $clone_exempt_mask "${nick}![getchanhost $nick $chan]"]} { return 0 } } }
  if {![info exists clone_type1_count($hostchan)]} { set clone_type1_count($hostchan) 0 }
    lappend clone_nicks_join($hostchan) $nickuhost
   if {[incr clone_type1_count($hostchan)] >= [lindex [split $clone_join_flood :] 0]} {
    if {$clone_modelock_switch == 1} { clone:channel:lock $chan }; set clone_number 0
     foreach person $clone_nicks_join($hostchan) {
       lappend clone_list $person
      }
      set clone_list [list]
      foreach user [chanlist $chan] {
       if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
        lappend clone_list $user:[getchanhost $user $chan]
        }
      }
      set clone_list [lsort -unique $clone_list]
      if {[llength $clone_list] > 1} {
       foreach clone $clone_list {
        if {$clone_punishment_type == 2} {
         set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next
         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
         if {[onchan [lindex [split $clone :] 0] $chan]} {
          incr clone_number; clone:punishment $clone $chan $clone_number [llength $clone_list] 1
          }
         }
        }
      utimer 1 [list clone:delay:check [lindex [split $uhost @] 1] $chan $clone_number [llength $clone_list] 1]
     }
     utimer [lindex [split $clone_join_flood :] 1] [list clone_expire_join $hostchan $nickuhost]
     if {![info exists clone_type3_count($hostchan)]} { set clone_type3_count($hostchan) 0 }
      lappend clone_nicks_joinpart($hostchan) $nickuhost
     if {[incr clone_type3_count($hostchan)] >= [expr [lindex [split $clone_joinpart_flood :] 0]*2]} {
     if {$clone_modelock_switch == 1} { clone:channel:lock $chan }
      foreach person $clone_nicks_joinpart($hostchan) {
        lappend clone_list $person
       }
       set clone_list [list]
       foreach user [chanlist $chan] {
        if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
         lappend clone_list $user:[getchanhost $user $chan]
         }
       }
       foreach clone $clone_list {
        if {$clone_punishment_type == 2} {
         set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next
         putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
         if {[onchan [lindex [split $clone :] 0] $chan]} {
          clone:punishment $clone $chan 0 0 3
          }
        }
      utimer 1 [list clone:delay:check:flud [lindex [split $uhost @] 1] $chan 3]
     }
     utimer [lindex [split $clone_joinpart_flood :] 1] [list clone_expire_joinpart $hostchan $nickuhost]
     if {$clone_flyby_type == 1} {
     if {![info exists clone_flyby([string tolower $nick:$chan])]} {
      set clone_flyby([string tolower $nick:$chan]) 1
     }
     if {[info exists clone_flyby([string tolower $nick:$chan])]} {
      utimer $clone_flyby_time [list unset clone_flyby([string tolower $nick:$chan])]
      }
   }
   if {$clone_regexp_match_switch == 1} {
    foreach clone_regexp_match $clone_regexp_matches {
     if {[regexp {^$clone_regexp_match$} $nick]} {
      break; set clone_regexp_match_pattern $clone_regexp_match
      }
    }
    set clone_number 0; set clone_list [list]
     foreach user [chanlist $chan] {
      if {[regexp {^$clone_regexp_match_pattern$} $user]} {
       lappend clone_list $user:[getchanhost $user $chan]
       }
      }
      foreach person [chanlist $chan] {
        if {[string equal [lindex [split $uhost @] 1] [lindex [split [getchanhost $person $chan] @] 0]] && ([lsearch -exact [string tolower $clone_list] [string tolower $person]] == -1)} {
         lappend clone_list $person:[getchanhost $person $chan]
         }
       }
       if {[llength $clone_list] > $clone_regexp_clones} {
        foreach clone $clone_list {
         incr clone_number; set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next; clone:punishment $clone $chan $clone_number [llength $clone_list] 6
         putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
      }
   } elseif {$clone_regexp_match_switch == 2} {
      foreach clone_regexp_match $clone_regexp_matches {
       if {[regexp {^$clone_regexp_match$} $nick]} {
        break; set clone_regexp_match_pattern $clone_regexp_match
        }
      }
      set clone_number 0; set clone_list [list]
       foreach user [chanlist $chan] {
        if {[regexp {^$clone_regexp_match_pattern$} $user] && [string equal "~" [string equal [string index [lindex [split [getchanhost $user $chan] @] 0] 0]]]} {
         lappend clone_list $user:[getchanhost $user $chan]
         }
        }
        foreach person [chanlist $chan] {
         if {[string equal [lindex [split $uhost @] 1] [lindex [split [getchanhost $person $chan] @] 0]] && [string equal "~" [string equal [string index [lindex [split [getchanhost $person $chan] @] 0] 0]]] && ([lsearch -exact [string tolower $clone_list] [string tolower $person]] == -1)} {
          lappend clone_list $person:[getchanhost $person $chan]
          }
        }
        if {[llength $clone_list] > $clone_regexp_clones} {
        foreach clone $clone_list {
         incr clone_number; set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next; clone:punishment $clone $chan $clone_number [llength $clone_list] 6
         putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
      }
   }
}


proc clone:part:check {nick uhost hand chan reason} {
 global clone_nicks_part clone_modelock_switch clone_exempt_masks clone_bantime clone_flyby_reason_length
 global clone_exempt_flags clone_flyby_type clone_flyby clone_flyby_time clone_punishment_type
 global clone_exempt_nicks clone_unresolved_ident clone_banmask_type clone_nicks_joinpart
 global clone_part_flood clone_joinpart_flood clone_type2_count clone_type3_count
 set nickuhost [string tolower $nick:$uhost]; set hostchan [lindex [split [string tolower $uhost] @] 1]:[string tolower $chan]
 if {($clone_unresolved_ident == 1) && ![string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { return 0 }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 0]]]} { foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] { if {[matchattr $hand $clone_exempt_global_flag]} { return 0 } } }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 1]]]} { foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] { if {[matchattr $hand $clone_exempt_local_flag $chan]} { return 0 } } }
  foreach clone_exempt_nick $clone_exempt_nicks { if {[string match -nocase $clone_exempt_nick $nick]} { return 0 } }
  foreach clone_exempt_mask $clone_exempt_masks { if {[string match -nocase $clone_exempt_mask "${nick}![getchanhost $nick $chan]"]} { return 0 } }
  if {![info exists clone_type2_count($hostchan)]} { set clone_type2_count($hostchan) 0 }
   lappend clone_nicks_part($hostchan) $nickuhost
  if {[incr clone_type2_count($hostchan)] >= [lindex [split $clone_part_flood :] 0]} {
    set clone_number 0
     foreach person $clone_nicks_part($hostchan) {
       lappend clone_list $person
      }
      set clone_list [list]
      foreach user [chanlist $chan] {
       if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
        lappend clone_list $user:[getchanhost $user $chan]
        }
      }
      set clone_list [lsort -unique $clone_list]
      if {[llength $clone_list] > 0} {
       foreach clone $clone_list {
        if {$clone_punishment_type == 2} {
         set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next
         putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
        if {[onchan [lindex [split $clone :] 0] $chan]} {
         incr clone_number; clone:punishment $clone $chan $clone_number [llength $clone_list] 2
         }
        }
       }
      utimer 1 [list clone:delay:check [lindex [split $uhost @] 1] $chan $clone_number [llength $clone_list] 2]
     }
     utimer [lindex [split $clone_part_flood :] 1] [list clone_expire_part $hostchan $nickuhost]
     if {![info exists clone_type3_count($hostchan)]} { set clone_type3_count($hostchan) 0 }
      lappend clone_nicks_joinpart($hostchan) $nickuhost
     if {[incr clone_type3_count($hostchan)] >= [expr [lindex [split $clone_joinpart_flood :] 0]*2]} {
     if {$clone_modelock_switch == 1} { clone:channel:lock $chan }
      foreach person $clone_nicks_joinpart($hostchan) {
        lappend clone_list $person
       }
       set clone_list [list]
       foreach user [chanlist $chan] {
        if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
         lappend clone_list $user:[getchanhost $user $chan]
         }
       }
       foreach clone $clone_list {
        if {$clone_punishment_type == 2} {
         set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
         putquick "MODE $chan +b $clone_banmask" -next         if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }}
         putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
         if {[onchan [lindex [split $clone :] 0] $chan]} {
          clone:punishment $clone $chan 0 0 3
          }
        }
      utimer 1 [list clone:delay:check:flud [lindex [split $uhost @] 1] $chan 3]
    }
    utimer [lindex [split $clone_joinpart_flood :] 1] [list clone_expire_joinpart $hostchan $nickuhost]
    if {$clone_flyby_type == 1} {
     if {[getchanjoin $nick $chan] >= [expr [clock seconds] - $clone_flyby_time]} {
     if {[string trim [string length $reason]] < $clone_flyby_reason_length} { return 0 }
     if {$clone_punishment_type == 2} {
      set clone_banmask [clone:banmask $nick!$uhost $clone_banmask_type]
      putquick "MODE $chan +b $clone_banmask" -next
      putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
      if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
     }
    if {[onchan $nick $chan]} {
     clone:punishment $nick:$uhost $chan 0 0 4
    }
    set clone_list [list]
     foreach user [chanlist $chan] {
       if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
        lappend clone_list $user:[getchanhost $user $chan]
        }
      }
      if {[llength $clone_list] > 0} {
       foreach clone $clone_list {
        clone:punishment $nick:$uhost $chan 0 0 4
        }
      }
    utimer 1 [clone:delay:check:flud [lindex [split $uhost @] 1] $chan 4]
   }
   if {[info exists clone_flyby([string tolower $nick:$chan])]} {
    putquick "MODE $chan +b [clone:banmask $nick!$uhost $clone_banmask_type]" -next
    putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
   if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
   if {[info exists clone_flyby([string tolower $nick:$chan])]} {
    unset clone_flyby([string tolower $nick:$chan])
   }
   if {[onchan $nick $chan]} {
    clone:punishment $nick:$uhost $chan 0 0 4
   }
   set clone_list [list]
    foreach user [chanlist $chan] {
     if {[string equal -nocase [lindex [split [getchanhost $user $chan] @] 1] [lindex [split $uhost @] 1]]} {
       lappend clone_list $user:[getchanhost $user $chan]
       }
     }
     if {[llength $clone_list] > 0} {
      foreach clone $clone_list {
       clone:punishment $nick:$uhost $chan 0 0 4
       }
      }
     utimer 1 [clone:delay:check:flud [lindex [split $uhost @] 1] $chan 4]
    }
  }
}


proc clone:delay:check {mask chan number total type} {
 foreach clone [chanlist $chan] {
  if {[string equal -nocase $mask [lindex [split [getchanhost $clone $chan] @] 1]]} {
    incr number; incr total; clone:punishment $clone:[getchanhost $clone $chan] $chan $number $total $type
    }
  }
}

proc clone:delay:check:flud {mask chan type} {
 foreach clone [chanlist $chan] {
  if {[string equal -nocase $mask [lindex [split [getchanhost $clone $chan] @] 1]]} {
    clone:punishment $clone:[getchanhost $clone $chan] $chan 0 0 $type
    }
  }
}

proc clone:channel:lock {chan} {
 global clone_primary_modelock clone_secondary_modelock clone_modelock_time clone_channel_locked
 global clone_notify_switch clone_notify_message_type clone_notify_type clone_notify_message
  if {$clone_primary_modelock == ""} { set clone_primary_modelock m }
  if {![info exists clone_channel_locked($chan)] && ![string match *$clone_primary_modelock* [getchanmode $chan]]} {
   putquick "MODE $chan +$clone_primary_modelock$clone_secondary_modelock" -next
   utimer $clone_modelock_time [list putquick "MODE $chan -$clone_primary_modelock$clone_secondary_modelock" -next]
   utimer [expr $clone_modelock_time + 3] [list unset clone_channel_locked($chan)]
   set clone_channel_locked($chan) "locked"
   if {$clone_notify_switch == 1} {
    switch -- $clone_notify_message_type {
     1 { set clone_notify "PRIVMSG $chan" }
     2 { set clone_notify "NOTICE $chan" }
     3 { set clone_notify "NOTICE @$chan" }
     default { set clone_notify "NOTICE $chan" }
    }
    if {$clone_notify_type == 1} { set clone_notify_string "$clone_notify :CLONE FLOOD: Channel has been locked to +$clone_primary_lockmode$clone_secondary_modelock for $clone_modelock_time seconds as suspected clones are detected."; putquick $clone_notify_string -next
     } elseif {($clone_notify_type == 2) && ($clonelocknotice != "")} { set clone_notify_string "$clone_notify :$clone_notify_message"; putquick $clone_notify_string -next
     } else { set clone_notify_string "$clone_notify :CLONE FLOOD: Channel has been locked to +$clone_primary_lockmode$clone_secondary_modelock for $clone_modelock_time seconds as suspected clones are detected."; putquick $clone_notify_string -next
      }
    }
  }
}

proc clone_expire_join {hostchan nickuhost} {
 global clone_type1_count clone_nicks_join
  incr clone_type1_count($hostchan) -1
  set clone_nicks_join($hostchan) [lreplace $clone_nicks_join($hostchan) [lsearch -exact $clone_nicks_join($hostchan) $nickuhost] [lsearch -exact $clone_nicks_join($hostchan) $nickuhost]]
  return
}

proc clone_expire_part {hostchan nickuhost} {
 global clone_type2_count clone_nicks_part
  incr clone_type2_count($hostchan) -1
  set clone_nicks_part($hostchan) [lreplace $clone_nicks_part($hostchan) [lsearch -exact $clone_nicks_part($hostchan) $nickuhost] [lsearch -exact $clone_nicks_part($hostchan) $nickuhost]]
  return
}

proc clone_expire_joinpart {hostchan nickuhost} {
 global clone_type3_count clone_nicks_joinpart
  incr clone_type3_count($hostchan) -1
  set clone_nicks_joinpart($hostchan) [lreplace $clone_nicks_joinpart($hostchan) [lsearch -exact $clone_nicks_joinpart($hostchan) $nickuhost] [lsearch -exact $clone_nicks_joinpart($hostchan) $nickuhost]]
  return
}


proc clone:remover:module {nick uhost hand chan} {
 global clone_banmask_type clone_punishment_type clone_bantime clone_unresolved_ident clone_channels
 global clone_channel_type clone_remover_type clone_max_allowed clone_exempt_flags clone_exempt_nicks clone_exempt_masks
 if {[isbotnick $nick] || ![botisop $chan] || ($clone_channel_type == 1) && ([lsearch -exact [split [string tolower $clone_channels]] [string tolower $chan]] == -1)} { return 0 }
 if {($clone_unresolved_ident == 1) && ![string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { return 0 }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 0]]]} { foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] { if {[matchattr $hand $clone_exempt_global_flag]} { return 0 } } }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 1]]]} { foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] { if {[matchattr $hand $clone_exempt_local_flag $chan]} { return 0 } } }
  foreach clone_exempt_nick $clone_exempt_nicks { if {[string match -nocase $clone_exempt_nick $nick]} { return 0 } }
  foreach clone_exempt_mask $clone_exempt_masks { if {[string match -nocase $clone_exempt_mask "${nick}![getchanhost $nick $chan]"]} { return 0 } }
  switch -- $clone_remover_type {
   1 {
       set clone_list [list]; set clone_banmask [clone:banmask $nick!$uhost $clone_banmask_type]
        foreach user [chanlist $chan] {
         if {[string match -nocase *$clone_banmask* "${nick}![getchanhost $user $chan]"]} {
          lappend clone_list $user:$uhost
          }
       }
       if {[llength $clone_list] > $clone_max_allowed} {
       set clone_number 0
        foreach person $clone_list {
         if {$clone_punishment_type == 2} {
          putquick "MODE $chan +b $clone_banmask" -next
          putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
          if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
         }
        incr clone_number; clone:punishment $person $chan $clone_number [llength $clone_list] 5
       }; set clone_list ""
     }
   }
   2 {
      set clone_list [list]
       foreach user [chanlist $chan] {
        foreach person [chanlist $chan] {
         if {[string match -nocase *[clone:banmask $person![getchanhost $person $chan] $clone_banmask_type]* "${user}![getchanhost $user $chan]"] && ![string equal -nocase $person $user]} {
          lappend clone_list $person:[getchanhost $person $chan]; set clone_banmask [clone:banmask $person![getchanhost $person $chan] $clone_banmask_type]
          }
        }
        if {[llength $clone_list] > $clone_max_allowed} {
         set clone_number 0
          foreach person $clone_list {
           if {$clone_punishment_type == 2} {
             putquick "MODE $chan +b $clone_banmask" -next
             putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
             if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
            }
           incr clone_number; clone:punishment $person $chan $clone_number [llength $clone_list] 5
          }
        }; set clone_list ""
      }; unset clone_list
    }
   default { return 0 }
  }
}


proc clone:remover:scan {} {
 global clone_remover_type clone_max_allowed clone_exempt_flags clone_exempt_nicks clone_exempt_masks
 global clone_remover_time clone_banmask_type clone_remover_type clone_unresolved_ident
 global clone_channel_type clone_channels clone_max_allowed clone_punishment_type
 switch -- $clone_channel_type {
  1 { set channels [channels] }
  2 { set channels [split $clone_channels] }
  default { return 0 }
 }
 set clone_list [list]
 foreach chan $channels {
  foreach user [chanlist $chan] {
   foreach person [chanlist $chan] {
    if {[info exists clone_ident]} { unset clone_ident }; if {[info exists clone_global_flag]} { unset clone_global_flag }; if {[info exists clone_local_flag]} { unset clone_local_flag }
    if {[info exists clone_nick]} { unset clone_nick }; if {[info exists clone_mask]} { unset clone_mask }
    if {[string match -nocase *[clone:banmask $person![getchanhost $person $chan] $clone_banmask_type]* "${user}![getchanhost $user $chan]"] && ![string equal -nocase $person $user]} {
     lappend clone_list $person:[getchanhost $person $chan]; set clone_banmask [clone:banmask $person![getchanhost $person $chan] $clone_banmask_type]
    } elseif {($clone_unresolved_ident == 1) && ![string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { set clone_ident 1
    } elseif {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 0]]]} { foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] { if {[matchattr $hand $clone_exempt_global_flag]} { set clone_global_flag 1 } }
    } elseif {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 1]]]} { foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] { if {[matchattr $hand $clone_exempt_local_flag $chan]} { set clone_local_flag 1 } }
    } elseif {$clone_exempt_nicks != ""} { foreach clone_exempt_nick $clone_exempt_nicks { if {[string match -nocase $clone_exempt_nick $nick]} { set clone_nick 1 } }
    } elseif {$clone_exempt_masks != ""} { foreach clone_exempt_mask $clone_exempt_masks { if {[string match -nocase $clone_exempt_mask "${nick}![getchanhost $nick $chan]"]} { set clone_mask 1 } }
  }
  if {![info exists clone_ident]} { set clone_ident 0 }; if {![info exists clone_global_flag]} { set clone_global_flag 0 }; if {![info exists clone_local_flag]} { set clone_local_flag 0 }
  if {![info exists clone_nick]} { set clone_nick 0 }; if {![info exists clone_mask]} { set clone_mask 0 }
  if {($clone_ident == 0) && ($clone_global_flag == 0) && ($clone_local_flag == 0) && ($clone_nick == 0) && ($clone_mask == 0) && ([llength $clone_list] > $clone_max_allowed)} {
   set clone_number 0
    foreach person $clone_list {
     if {$clone_punishment_type == 2} {
       putquick "MODE $chan +b $clone_banmask" -next
       if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
       }
      incr clone_number; clone:punishment $person $chan $clone_number [llength $clone_list] 5
      }
     }; set clone_list ""
    }; unset clone_list
   }
  }
 if {($clone_remover_type == 3) && ![string match "*clone:remover:scan*" [timers]]} { timer $clone_remover_time clone:remover:scan }
}


proc clone:punishment {clone chan number total type} {
 global clone_kick_user2_msg clone_banmask_type clone_bantime clone_kick_counter_type clone_kick_user6_msg
 global clone_kick_counter_file clone_punishment_type clone_exempt_ops_voices clone_kick_user1_msg
 global clone_kick_msg_type clone_kick_user3_msg clone_kick_user4_msg clone_kick_user5_msg
 global clone_unresolved_ident clone_exempt_flags clone_exempt_masks clone_exempt_nicks
 if {$clone_kick_counter_type == 1} {
  if {![file exists $clone_kick_counter_file]} {
   create:clone:kick:file $clone_kick_counter_file
  } else {
   set file [open $clone_kick_counter_file "r"]; set current_kicks [gets $file]
   if {$current_kicks == ""} { set current_kicks 1 }
   close $file; set clone_kicks $current_kicks
   }
  }
  set nick [lindex [split $clone :] 0]; set uhost [lindex [split $clone :] 1]
  set clone_banmask [clone:banmask [string map {: !} $clone] $clone_banmask_type]
  switch -- $type {
   1 {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Clone Join Flood 12,0 - Mass 2joining clones 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user1_msg }
        default { set clone_kick_msg "0,1 Clone Join Flood 12,0 - 12Mass 2joining clones 12detected 2from 12host 6$clone_banmask" }
        }
   }
   2 {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Clone Part Flood 12,0 - Mass 2parting clones 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user2_msg }
        default { set clone_kick_msg "0,1 Clone Part Flood 12,0 - 12Mass 2parting clones 12detected 2from 12host 6$clone_banmask" }
        }
   }
   3 {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Clone Join/Part Flood 12,0 - 12Mass 2join/part revolving door flood 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user3_msg }
        default { set clone_kick_msg "0,1 Clone Join/Part Flood 12,0 - 12Mass 2join/part revolving door flood 12detected 2from 12host 6$clone_banmask" }
        }
   }
   4 {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Excessive Clones 12,0 - Excessive 2clones residing 12in channel 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user4_msg }
        default { set clone_kick_msg "0,1 Excessive Clones 12,0 - Excessive 2clones residing 12in channel 12detected 2from 12host 6$clone_banmask" }
        }
   }
   5  {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Fast Join/Part Flood 12,0 - Fast 2join/part flood 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user5_msg }
        default { set clone_kick_msg "0,1 Fast Join/Part Flood 12,0 - Fast 2join/part flood 12detected 2from 12host 6$clone_banmask" }
        }
   }
   6 {
       switch -- $clone_kick_msg_type {
        0 { set clone_kick_msg "0,1 Suspected Mass Joining Clones 12,0 - Suspected 2ordered nick 12botnet 12detected 2from 12host 6$clone_banmask" }
        1 { set clone_kick_msg $clone_kick_user6_msg }
        default { set clone_kick_msg "0,1 Suspected Mass Joining Clones 12,0 - Suspected 2ordered nick 12botnet 12detected 2from 12host 6$clone_banmask" }
        }
     }
    default { set clone_kick_msg "0,1 Clone Flood 12,0 - Mass 2join, join/part, revolving door, excessive clones 12flood 2from 12host 6$clone_banmask" }
   }
   if {$total > 1} { set clone_kick_msg "$clone_kick_msg -2 (Clone 12$number 2of 12$total2)" }
   if {[string match "*(Clone * of *)*" [clone:strip:codes $clone_kick_msg]]} {
   switch -- $clone_kick_counter_type {
    0 { set clone_kick_msg $clone_kick_msg }
    1 { set clone_kick_msg "$clone_kick_msg 2,0(Kick #$clone_kicks)" }
    default { set clone_kick_msg $clone_kick_msg }
    }
   } else {
   switch -- $clone_kick_counter_type {
    0 { set clone_kick_msg $clone_kick_msg }
    1 { set clone_kick_msg "$clone_kick_msg 2,0- (Kick #$clone_kicks)" }
    default { set clone_kick_msg $clone_kick_msg }
    }
   }
   set message $clone_kick_msg
   if {($clone_punishment_type == 1) || ($clone_punishment_type == 2)} {
    if {($clone_exempt_ops_voices == 1) || ($clone_exempt_ops_voices == 2) && ![isvoice $nick $chan] || ($clone_exempt_ops_voices == 3) && ![isop $nick $chan] || ($clone_exempt_ops_voices == 4) && ![isvoice $nick $chan] && ![isop $nick $chan]} {
     if {[info exists clone_ident]} { unset clone_ident }; if {[info exists clone_global_flag]} { unset clone_global_flag }; if {[info exists clone_local_flag]} { unset clone_local_flag }
     if {[info exists clone_nick]} { unset clone_nick }; if {[info exists clone_mask]} { unset clone_mask }
     if {($clone_unresolved_ident == 1) && [string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { set clone_ident 1 }
      if {([string trim [lindex [split $clone_exempt_flags |] 0]] != "-") && ([string trim [lindex [split $clone_exempt_flags |] 0]] != "")} {
      foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] {
       if {[matchattr [nick2hand $nick $chan] $clone_exempt_global_flag]} {
        break; set clone_global_flag 1
        }
       }
      }
      if {([string trim [lindex [split $clone_exempt_flags |] 1]] != "-") && ([string trim [lindex [split $clone_exempt_flags |] 1]] != "")} {
       foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] {
        if {[matchattr [nick2hand $nick $chan] $clone_exempt_local_flag $chan]} {
         break; set clone_local_flag 1
         }
        }
       }
       if {$clone_exempt_nicks != ""} {
       foreach clone_exempt_nick $clone_exempt_nicks {
        if {[string match -nocase $clone_exempt_nick $nick]} {
         break; set clone_nick 1
         }
        }
       }
       if {$clone_exempt_masks != ""} {
       foreach clone_exempt_mask $clone_exempt_masks {
        if {[string match -nocase *$clone_exempt_mask* "${nick}![getchanhost $nick $chan]"]} {
         break; set clone_mask 1
         }
        }
       }
       if {![info exists clone_ident]} { set clone_ident 0 }; if {![info exists clone_global_flag]} { set clone_global_flag 0 }; if {![info exists clone_local_flag]} { set clone_local_flag 0 }
       if {![info exists clone_nick]} { set clone_nick 0 }; if {![info exists clone_mask]} { set clone_mask 0 }
       if {($clone_ident == 0) && ($clone_global_flag == 0) && ($clone_local_flag == 0) && ($clone_nick == 0) && ($clone_mask == 0)} {
        do:clone:kick $nick $chan $message
        }
      }
    } elseif {$clone_punishment_type == 3} {
       newchanban $chan $clone_banmask clones $message $clone_bantime; return 0
    } elseif {$clone_punishment_type == 4} {
       newban $clone_banmask clones $message $clone_bantime; return 0
    } else {
       if {($clone_exempt_ops_voices == 1) || ($clone_exempt_ops_voices == 2) && ![isvoice $nick $chan] || ($clone_exempt_ops_voices == 3) && ![isop $nick $chan] || ($clone_exempt_ops_voices == 4) && ![isvoice $nick $chan] && ![isop $nick $chan]} {
        if {[info exists clone_ident]} { unset clone_ident }; if {[info exists clone_global_flag]} { unset clone_global_flag }; if {[info exists clone_local_flag]} { unset clone_local_flag }
        if {[info exists clone_nick]} { unset clone_nick }; if {[info exists clone_mask]} { unset clone_mask }
        if {($clone_unresolved_ident == 1) && [string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { set clone_ident 1 }
        if {([string trim [lindex [split $clone_exempt_flags |] 0]] != "-") && ([string trim [lindex [split $clone_exempt_flags |] 0]] != "")} {
         foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] {
          if {[matchattr [nick2hand $nick $chan] $clone_exempt_global_flag]} {
           break; set clone_global_flag 1
           }
          }
         }
         if {([string trim [lindex [split $clone_exempt_flags |] 1]] != "-") && ([string trim [lindex [split $clone_exempt_flags |] 1]] != "")} {
          foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] {
           if {[matchattr [nick2hand $nick $chan] $clone_exempt_local_flag $chan]} {
            break; set clone_local_flag 1
            }
           }
          }
          if {$clone_exempt_nicks != ""} {
          foreach clone_exempt_nick $clone_exempt_nicks {
           if {[string match -nocase $clone_exempt_nick $nick]} {
            break; set clone_nick 1
            }
           }
          }
          if {$clone_exempt_masks != ""} {
           foreach clone_exempt_mask $clone_exempt_masks {
            if {[string match -nocase *$clone_exempt_mask* "${nick}![getchanhost $nick $chan]"]} {
             break; set clone_mask 1
             }
            }
           }
           if {![info exists clone_ident]} { set clone_ident 0 }; if {![info exists clone_global_flag]} { set clone_global_flag 0 }; if {![info exists clone_local_flag]} { set clone_local_flag 0 }
           if {![info exists clone_nick]} { set clone_nick 0 }; if {![info exists clone_mask]} { set clone_mask 0 }
           if {($clone_ident == 0) && ($clone_global_flag == 0) && ($clone_local_flag == 0) && ($clone_nick == 0) && ($clone_mask == 0)} {
            putquick "MODE $chan +b $clone_banmask" -next
            putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
            do:clone:kick $nick $chan $message
            }
        }
    }
}


proc do:clone:kick {nick chan message} {
 global clone_kick_queue clone_queue_next_option
  switch -- $clone_kick_queue {
   1 {
       switch -- $clone_queue_next_option {
        0 { puthelp "KICK $chan $nick :$message" }
        1 { puthelp "KICK $chan $nick :$message" -next }
        default { puthelp "KICK $chan $nick :$message" -next }
        }
   }
   2 {
       switch -- $clone_queue_next_option {
        0 { putserv "KICK $chan $nick :$message" }
        1 { putserv "KICK $chan $nick :$message" -next }
        default { putserv "KICK $chan $nick :$message" -next }
        }
   }
   3 {
       putkick $chan $nick $message
   }
   4 {
       switch -- $clone_queue_next_option {
        0 { putserv "KICK $chan $nick :$message" }
        1 { putserv "KICK $chan $nick :$message" -next }
        default { putserv "KICK $chan $nick :$message" -next }
        }
   }
   5 {
       switch -- $clone_queue_next_option {
        0 { putquick "KICK $chan $nick :$message" }
        1 { putquick "KICK $chan $nick :$message" -next }
        default { putquick "KICK $chan $nick :$message" -next }
        }
   }
   6 { putdccraw 0 [string length "KICK $chan $nick :$message\n"] "KICK $chan $nick :$message\n" }
   default {
     switch -- $clone_queue_next_option {
      0 { putquick "KICK $chan $nick :$message" }
      1 { putquick "KICK $chan $nick :$message" -next }
      default { putquick "KICK $chan $nick :$message" -next }
      }
    }
  }
}


proc clone:channel:unban {mask chan} {
 if {[botisop $chan] && [ischanban $mask $chan]} {
  pushmode $chan -b $mask
  }
}

proc clone:clear:utimers {type} {
 foreach utimer [utimers] {
 if {[string match -nocase "*clone_*_count*" [lindex $utimer 1]]} {
  killutimer [lindex $utimer 2]
  }
 }
}


proc clone:join:flood:check {nick uhost hand chan} {
 global clone_channels clone_modelock_switch clone_kick_type clone_kick_msg_type clone_exempt_hosts clone_channel_type clone_banmask_type
 global clone_exempt_hosts clone_exempt_flags clone_exempt_ops clone_exempt_voices clone_unresolved_ident clone_punishment_type clone_bantime
 if {[isbotnick $nick] || ![botisop $chan] || ($clone_channel_type == 1) && ([lsearch -exact [split [string tolower $clone_channels]] [string tolower $chan]] == -1)} { return 1 }
 if {($clone_unresolved_ident == 1) && ![string equal "~" [string index [lindex [split $uhost @] 0] 0]]} { return 1 }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 0]]]} { foreach clone_exempt_global_flag [split [string trim [lindex [split $clone_exempt_flags |] 0]] ""] { if {[matchattr $hand $clone_exempt_global_flag]} { return 1 } } }
  if {![string equal "-" [string trim [lindex [split $clone_exempt_flags |] 1]]]} { foreach clone_exempt_local_flag [split [string trim [lindex [split $clone_exempt_flags |] 1]] ""] { if {[matchattr $hand $clone_exempt_local_flag $chan]} { return 1 } } }
  if {$clone_exempt_nicks != ""} { foreach clone_exempt_nick $clone_exempt_nicks { if {[string match -nocase $clone_exempt_nick $nick]} { return 1 } } }
  if {$clone_exempt_masks != ""} { foreach clone_exempt_mask $clone_exempt_masks { if {[string match -nocase $clone_exempt_mask "${nick}![getchanhost $nick $chan]"]} { return 1 } } }
  if {$clone_modelock_switch == 1} { clone:channel:lock $chan }
  if {$clone_punishment_type == 2} {
   set clone_banmask [clone:banmask $nick!$uhost $clone_banmask_type]
   putquick "MODE $chan +b $clone_banmask" -next
   putquick "privmsg X :ban $chan $clone_banmask 1 100 Please remove your clones from your channel" 
   if {$clone_bantime > 0} { timer $clone_bantime [list clone:channel:unban $clone_banmask $chan] }
  }
  set clone_list [list]; set clone_number 0
   foreach user [chanlist $chan] {
    if {[string equal -nocase [lindex [split $uhost @] 1] [lindex [split [getchanhost $user $chan] @] 1]]} {
     lappend clone_list $user:[getchanhost $user $chan]
     }
   }
   foreach clone $clone_list {
    if {[llength $clone_list] > 1} {
     incr clone_number; clone:punishment $clone $chan $clone_number [llength $clone_list] 1
    } else {
      clone:punishment $clone $chan 0 0 1
      }
    }
  utimer 1 [list clone:delay:check:flud [lindex [split $uhost @] 1] $chan 1]; return 1
}


proc clone:strip:codes {string} {
 global numversion
  if {$numversion >= 1061700}  {
   set string [stripcodes bcruag $string]
  } else {
   regsub -all -- {\003([0-9]{1,2}(,[0-9]{1,2})?)?} $string "" string
   set string [string map {"\017" "" "\037" "" "\002" "" "\026" "" "\006" "" "\007" ""} $string]
  }
}

proc clone:banmask [list name [list type $clone_banmask_type]] {
   if {[scan $name {%[^!]!%[^@]@%s} nick user host]!=3} {
      error "Usage: clone:banmask <nick!user@host> \[type\]"
   }
   if {![string is int -strict $type] || $type < 0 || $type > 29} {
      error "Invalid ban type."
   }
   set ip [string match {*[0-9]} $host]
   if {$type < 10 || $ip} {
      if {[string match {[3489]} [expr {$type%10}]]} {
         if $ip {
            set host [join [lrange [split $host .] 0 2] .].*
         } elseif {[string match *.*.* $host]} {
            set host *.[join [lrange [split $host .] end-1 end] .]
         }
      }
   } elseif {$type > 19} {
      regsub -all {[0-9]+} $host * host
   } else {
      regsub -all {[0-9]} $host ? host
   }
   set type [expr {$type%10}]
   if [string match {[1368]} $type] {
      set user *[string trimleft $user ~]
   } elseif {[string match {[2479]} $type]} {
      set user *
   }
   if [string match {[01234]} $type] {
      set nick *
   }
  return $nick!$user@$host
}


proc create:clone:kick:file {clonekickfile} {
  putlog "\[MASS JOIN/PART CLONE PROTECTION\] ERROR: The \002clone 'kick counter' file\002 does not exist!"
  putlog "Creating clone kick counter file: \002$clonekickfile\002"
  set file [open $clonekickfile "w"]
  puts $file 1; close $file
}

proc clone:kick:counter {nick uhost handle chan target reason} {
 global clone_kick_counter_file clone_kick_counter_type
  if {[isbotnick $nick] && ![isbotnick $target] && ($clone_kick_counter_type == 1)} {
   if {![file exists $clone_kick_counter_file]} {
    create:clone:kick:file $clone_kick_counter_file
   } elseif {[string match -nocase "*(Clone * of *)*" $reason]} {
    set file [open $clone_kick_counter_file "r"]
    set currentkicks [gets $file]; close $file
    set file [open $clone_kick_counter_file "w"]
    puts $file [expr $currentkicks + 1]; close $file
    }
  }
}


putlog "\[LOADED\] Mass Clone Join/Part and Revolving Door Protection v4.24.b by \002awyeah (awyeah@awyeah.org)\002"
if {$clone_channel_type == 1} { putlog "ACTIVE: Mass Clone Join/Part and Revolving Door Protection is ENABLED on: \002[split [string tolower $clone_channels]]\002"
} elseif {$clone_channel_type == 2} { putlog "ACTIVE: Mass Clone Join/Part and Revolving Door Protection is ENABLED on: \002All channels the bot is on\002"
} else { putlog "\[UNLOADED\] Mass Clone Join/Part and Revolving Door Protection: \002No channel type selected.\002" }

putlog "--------------------------------------------------------"
putlog " MASS CLONE JOIN/PART PROTECTION - MAIN SCRIPT SETTINGS"
putlog "--------------------------------------------------------"
if {[string match *0* $clone_join_flood] || ($clone_join_flood == "")} {
 putlog "Clone join protection: \002Disabled\002"
} else {
 putlog "Clone join protection: \002[lindex [split $clone_join_flood :] 0] or more joins in less than [lindex [split $clone_join_flood :] 1] seconds\002"
}
if {[string match *0* $clone_part_flood] || ($clone_part_flood == "")} {
 putlog "Clone part protection: \002Disabled\002"
} else {
 putlog "Clone part protection: \002[lindex [split $clone_part_flood :] 0] or more parts in less than [lindex [split $clone_part_flood :] 1] seconds\002"
}
if {[string match *0* $clone_joinpart_flood] || ($clone_joinpart_flood == "")} {
 putlog "Clone revolving door protection: \002Disabled\002"
} else {
 putlog "Clone revolving door protection: \002[lindex [split $clone_joinpart_flood :] 0] or more join/part cycles in less than [lindex [split $clone_joinpart_flood :] 1] seconds\002"
}
if {$clone_modelock_switch == 0} {
 putlog "Channel lock protection: \002Disabled\002"
} elseif {$clone_modelock_switch == 1} {
 putlog "Channel lock protection: \002Enabled\002"
  if {$clone_primary_modelock != ""} {
   putlog "Channel mode lock: \002+$clone_primary_modelock$clone_secondary_modelock\002"
  } else {
   putlog "Channel mode lock: \002+m\002"
   }
}
if {$clone_notify_switch == 0} {
 putlog "Channel lock notification: \002Disabled\002"
} elseif {$clone_notify_switch == 1} {
 putlog "Channel lock notification: \002Enabled\002"
  if {$clone_notify_message_type == 1} {
   putlog "Channel lock notification type: \002CHANNEL TEXT\002"
  } elseif {$clone_notify_message_type == 2} {
   putlog "Channel lock notification type: \002CHANNEL NOTICE\002"
  } elseif {$clone_notify_message_type == 3} {
   putlog "Channel lock notification type: \002CHANNEL OP-NOTICE\002"
   }
}
if {$clone_exempt_ops == 0} {
 putlog "Channel operators exemption: \002Disabled\002"
} elseif {$clone_exempt_ops == 1} {
 putlog "Channel operators exemption: \002Enabled\002"
}
if {$clone_exempt_voices == 0} {
 putlog "Channel voices exemption: \002Disabled\002"
} elseif {$clone_exempt_voices == 1} {
 putlog "Channel voices exemption: \002Enabled\002"
}
if {[string equal "-" $clone_exempt_flags] || [string equal "-|-" $clone_exempt_flags] || ($clone_exempt_flags == "")} {
 putlog "User flags exemption: \002Disabled\002"
} else {
 putlog "User flags exemption: \002Enabled\002"
 putlog "User flags: \002$clone_exempt_flags\002"
}
if {$clone_banmask_type > 0} {
 if {[string equal "1" [string length $clone_banmask_type]]} {
  if {$clone_banmask_type == 0} {
   putlog "Banmask type: \002*!user@host.domain\002"
  } elseif {$clone_banmask_type == 1} {
   putlog "Banmask type: \002*!*user@host.domain\002"
  } elseif {$clone_banmask_type == 2} {
   putlog "Banmask type: \002*!*@host.domain\002"
  } elseif {$clone_banmask_type == 3} {
   putlog "Banmask type: \002*!*user@*.domain\002"
  } elseif {$clone_banmask_type == 4} {
   putlog "Banmask type: \002*!*@*.domain\002"
  } elseif {$clone_banmask_type == 5} {
   putlog "Banmask type: \002nick!user@host.domain\002"
  } elseif {$clone_banmask_type == 6} {
   putlog "Banmask type: \002nick!*user@host.domain\002"
  } elseif {$clone_banmask_type == 7} {
   putlog "Banmask type: \002nick!*@host.domain\002"
  } elseif {$clone_banmask_type == 8} {
   putlog "Banmask type: \002nick!*user@*.domain\002"
  } elseif {$clone_banmask_type == 9} {
   putlog "Banmask type: \002nick!*@*.domain\002"
  }
 } elseif {[string equal "2" [string length $clone_banmask_type]]} {
  if {[string equal "0" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002*!user@host.domain\002"
  } elseif {[string equal "1" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002*!*user@host.domain\002"
  } elseif {[string equal "2" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002*!*@host.domain\002"
  } elseif {[string equal "3" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002*!*user@*.domain\002"
  } elseif {[string equal "4" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002*!*@*.domain\002"
  } elseif {[string equal "5" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002nick!user@host.domain\002"
  } elseif {[string equal "6" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002nick!*user@host.domain\002"
  } elseif {[string equal "7" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002nick!*@host.domain\002"
  } elseif {[string equal "8" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002nick!*user@*.domain\002"
  } elseif {[string equal "9" [string index $clone_banmask_type 1]]} {
   set clone_display_banmask "Banmask type: \002nick!*@*.domain\002"
  }
  if {[string equal "1" [string index $clone_banmask_type 0]]} {
   set clone_banmask_wildcard "?"
  } elseif {[string equal "2" [string index $clone_banmask_type 0]]} {
   set clone_banmask_wildcard "*"
  }
  putlog "$clone_display_banmask \002(replacing numbers in the *!*@host with the '$clone_banmask_wildcard' wildcard)\002"
 }
}
if {$clone_punishment_type == 1} {
  putlog "Punishment type: \002Kick all clones from the channel\002"
} elseif {$clone_punishment_type == 2} {
  putlog "Punishment type: \002Kick and ban all clones from the channel\002"
} elseif {$clone_punishment_type == 3} {
  putlog "Punishment type: \002Kick and ban all clones from the channel - STICKY BAN\002"
} elseif {$clone_punishment_type == 4} {
  putlog "Punishment type: \002Kick and ban all clones and users matching clones *!*@host from all channels found on matching with the bot - GLOBAL BAN\002"
} else {
  putlog "Punishment type: \002Kick and ban all clones from the channel\002"
}
if {$clone_bantime == 0} {
  putlog "Ban time: \002PERMANENT\002"
} else {
  putlog "Ban time: \002$clone_bantime minutes\002"
}
if {$clone_kick_msg_type == 1} {
 putlog "Kick messages: \002User defined\002"
} else {
 putlog "Kick messages: \002Script defaults\002"
}
if {$clone_kick_counter_type == 0} {
 putlog "Kick counter: \002Disabled\002"
} elseif {$clone_kick_counter_type == 1} {
 putlog "Kick counter: \002Enabled\002"
}
putlog "------------------------------------------------------"
if {$clone_flyby_type == 0} {
 putlog "Flyby module: \002Disabled\002"
} elseif {$clone_flyby_type == 1} {
 putlog "Flyby module: \002Enabled\002"
 putlog "Maximum flyby time: \002$clone_flyby_time seconds\002"
  if {$clone_flyby_reason_length > 0} {
   putlog "Flyby part message length: \002$clone_flyby_reason_length characters\002"
  } else {
   putlog "Flyby part message length: \002Disabled\002"
   }
}
putlog "------------------------------------------------------"
if {$clone_remover_type == 0} {
  putlog "Clone remover module: \002Disabled\002"
} elseif {$clone_remover_type == 1} {
  putlog "Clone remover module: \002Scan channel for clones on user joins\002"
} elseif {$clone_remover_type == 2} {
  putlog "Clone remover module: \002Full channel scan for clones on user joins\002"
} elseif {($clone_remover_type == 3) && ($clone_remover_time > 0)} {
  putlog "Clone remover module: \002Full channel scan for clones every $clone_remover_time minutes\002"
}
if {$clone_max_allowed > 0} {
 putlog "Maximum clones allowed: \002$clone_max_allowed\002"
}
putlog "------------------------------------------------------"






