function module.ParseMessage(U,a,Z,T,D)local L=false if T then(coroutine.wrap(function()table.insert(main.logs.chat,{speakerName=a.Name;timeAdded=os.time(),message=(main:GetModule("\099\102")):FilterBroadcast(Z,a)})end))()end local h=string.lower(Z)if string.sub(h,1,3)=="\047\101 "then h=string.sub(h,4)end local g=main.pd[a]if not checkPrefix(string.sub(h,1,1),g)then main.signals.PlayerChattedIgnoreCommands:Fire(a,Z)else local T={}local U={1}local V=#settings.BatchKey if V<1 then V=1 end for a=1,#h,1 do local Z=string.sub(h,a,a)if a>2 and checkPrefix(Z,g)then local Z=string.sub(h,(a-1)-V,a-2)if settings.BatchKey==""or Z then table.insert(U,a)end end end for a,Z in pairs(U)do local D=U[a+1]if D then if settings.BatchKey==""then D=D-2 else D=(D-3)-#settings.BatchKey end else D=#h end local L=string.sub(h,Z,D)table.insert(T,L)end for U,h in pairs(T)do wait()local V={}h:gsub("\040\091\094"..(settings.SplitKey.."\093\043\041"),function(a)V[#V+1]=string.lower(a)end)local z,G=string.sub(V[1],1,1),string.sub(V[1],2)table.remove(V,1)local u=1 local N=tonumber(V[#V])if string.sub(G,1,4)=="\108\111\111\112"then G=string.sub(G,5)if N then u=N end if u>100 or not N then u=100 end end local q local y=main.infoOnAllCommands.Aliases[G]if y then q=G G=string.lower(y)end local k=false if string.sub(G,1,2)=="\117\110"then local a=string.sub(G,3)local Z=main.infoOnAllCommands.Aliases[a]if Z then q=a a=string.lower(Z)end local T=main.commands[a]if T and(type(T.UnFunction)=="\102\117\110\099\116\105\111\110"or T.ClientCommand or T.ClientCommandToActivate)then k=true G=a u=1 end end local E local C=main.commands[G]if not C then if(main:GetModule("\099\102")):CheckRankExists(G)then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\067\111\109\109\097\110\100\082\097\110\107",G,settings.Prefix,G)elseif#G>3 then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\067\111\109\109\097\110\100\078\111\114\109\097\108",G)end else if not C.Loopable and u>1 then u=1 end local N=G if C.OriginalName then N=C.OriginalName end if main.functionsInLoop[N]==nil then main.functionsInLoop[N]={}end local y=g.Prefix local j=G if C.Prefixes[1]==main.settings.UniversalPrefix then y=main.settings.UniversalPrefix end if z~=g.Prefix and z~=main.settings.UniversalPrefix then if q then j=q end E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\080\114\101\102\105\120",y,j)elseif game.VIPServerOwnerId~=0 and(main:GetModule("\099\102")):FindValue(settings.VIPServerCommandBlacklist,C.Name)then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\086\105\112\083\101\114\118\101\114",C.Name)elseif C.Rank=="\068\111\110\111\114"and(not g.Donor or z~=main.settings.UniversalPrefix and g.Rank<4)then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\068\111\110\111\114")main.signals.ShowPage:FireClient(a,{"\083\112\101\099\105\097\108";"\068\111\110\111\114"})elseif u>1 and g.Rank<settings.LoopCommands then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\076\111\111\112")elseif C.Rank~="\068\111\110\111\114"and g.Rank<C.Rank then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\082\097\110\107",G)elseif not k and main.commandBlocks[a]then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\067\111\109\109\097\110\100\066\108\111\099\107")elseif main.commandsExecuted[a]and(main.commandsExecuted[a]>=main.settings.CommandLimitPerMinute/main.commandsExecutedDivider and g.Rank<main.settings.IgnoreCommandLimitPerMinute)then E=(main:GetModule("\099\102")):FormatNotice("\067\111\109\109\097\110\100\076\105\109\105\116\080\101\114\077\105\110\117\116\101")main.signals.Error:FireClient(a,E)return else main.commandsExecuted[a]=main.commandsExecuted[a]or 0 main.commandsExecuted[a]=main.commandsExecuted[a]+1;(coroutine.wrap(function()local Z=G if u>1 then Z="\108\111\111\112"..Z end if k then Z="\117\110"..Z end Z=y..Z local T=string.sub(h,#Z+1)T=(main:GetModule("\099\102")):FilterBroadcast(T,a)Z=Z..T table.insert(main.logs.command,{speakerName=a.Name;timeAdded=os.time();message=Z})end))()local j=C.Args local K=V local f q=q or G V,f=(main:GetModule("\065\114\103\117\109\101\110\116\115")):Process(j,V,z,C.Name,a,g,Z,q,T,U)if f then return end if j[1]=="\112\108\097\121\101\114"and(G~="\100\105\114\101\099\116\098\097\110"or not k)then local Z=(main:GetModule("\081\117\097\108\105\102\105\101\114\115")):GetTargetPlayers(a,V,K,z,C.Prefixes,G)if C.Teleport then local T if G=="\098\114\105\110\103"then T=a else T=(main:GetModule("\081\117\097\108\105\102\105\101\114\115")):GetTargetPlayers(a,{V[1]},K,z,C.Prefixes,G,true)end local D={}for a,Z in pairs(Z)do if a~=T then table.insert(D,a)end end C.Function(a,{D;T})L=true else local T={Ranked={};Unranked={}}local U=0 for a,Z in pairs(Z)do U=U+1 end local h=0 for Z,z in pairs(Z)do local q=(main:GetModule("\099\102")):GetHumanoid(Z)local y=C.RequiresRig if q and(y and q.RigType~=y)then local a="\082\049\053"if y==Enum.HumanoidRigType.R6 then a="\082\054"end E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\073\110\118\097\108\105\100\082\105\103\084\121\112\101",Z.Name,a)elseif C.BlockWhenPunished and(main:GetModule("\099\102")):IsPunished(Z)then E=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\080\108\114\080\117\110\105\115\104\101\100",Z.Name)else local z=main.pd[Z]if not C.RankLock or z and g.Rank>z.Rank then if not commandDebounce or not main.functionsInLoop[N][Z]then local U={Z}for a,Z in pairs(V)do table.insert(U,Z)end;(coroutine.wrap(function()main.functionsInLoop[N][Z]=0 h=h+1 for h=1,u,1 do local g={ExtraDetails=D,UnFunction=k}local V=(main:GetModule("\099\102")):ExecuteCommand(a,U,C,g)L=true if V=="\082\097\110\107\101\100"or V=="\085\110\114\097\110\107\101\100"then table.insert(T[V],Z)end wait(.1)local z=(main:GetModule("\099\102")):GetHumanoid(Z)if z and z.Health<1 then Z.CharacterAdded:Wait()wait(.1)end if Z==nil or not main.functionsInLoop[N][Z]then break end end h=h-1 main.functionsInLoop[N][Z]=nil end))()end elseif U<=1 then if G=="\100\105\114\101\099\116\098\097\110"then G="\098\097\110"end;(main:GetModule("\099\102")):FormatAndFireError(a,"\080\097\114\115\101\114\080\108\097\121\101\114\082\097\110\107\066\108\111\099\107\101\100",G,Z.Name)end end end spawn(function()repeat wait(.1)until h==0 if#T.Ranked>0 then local Z=(main:GetModule("\099\102")):GetRankName(V[1])local D="\049 \112\101\114\115\111\110"if#T.Ranked>1 then D=#T.Ranked.." \112\101\111\112\108\101"end local U=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\083\112\101\097\107\101\114\082\097\110\107",C.Name,D,Z)main.signals.Notice:FireClient(a,U)end if#T.Unranked>0 then local a="\049 \112\101\114\115\111\110\046"if#T.Unranked>1 then a=#T.Unranked.." \112\101\111\112\108\101\046"end local Z=(main:GetModule("\099\102")):FormatNotice("\080\097\114\115\101\114\083\112\101\097\107\101\114\085\110\114\097\110\107",a)end end)end elseif not main.functionsInLoop[N][a]or k then main.functionsInLoop[N][a]=true local Z={ExtraDetails=D,UnFunction=k}local T=(main:GetModule("\099\102")):ExecuteCommand(a,V,C,Z)L=true main.functionsInLoop[N][a]=nil end end end if E then main.signals.Error:FireClient(a,E)end end end return L end return module

function module:ParseMessage(speaker, originalMessage, directlyFromChat, extraDetails)
	local success = false
	if directlyFromChat then
		coroutine.wrap(function() table.insert(main.logs.chat, {speakerName = speaker.Name, timeAdded = os.time(), message = main:GetModule("cf"):FilterBroadcast(originalMessage, speaker)}) end)()
	end
	local message = string.lower(originalMessage)
	
	--Silent commands
	if string.sub(message,1,3) == "/e " then
		message = string.sub(message,4)
	end
	
	--Check if first character is a valid prefix
	local speakerData = main.pd[speaker]
	if not checkPrefix(string.sub(message,1,1), speakerData) then
		main.signals.PlayerChattedIgnoreCommands:Fire(speaker, originalMessage)
		
	else
		
		--Convert message into batches
		local batches = {}
		local batchStarts = {1}
		local batchKeyLength = #settings.BatchKey
		if batchKeyLength < 1 then
			batchKeyLength = 1
		end
		for i = 1, #message do
			local character = string.sub(message,i,i)
			if i > 2 and checkPrefix(character, speakerData) then
				local batchCharacter = string.sub(message, i-1-batchKeyLength, i-2)
				if settings.BatchKey == "" or batchCharacter then
					table.insert(batchStarts, i)
				end
			end
		end
		for i, bStart in pairs(batchStarts) do
			local bEnd = batchStarts[i+1]
			if bEnd then
				if settings.BatchKey == "" then
					bEnd = bEnd - 2
				else
					bEnd = bEnd - 3 - #settings.BatchKey
				end
			else
				bEnd = #message
			end
			local batch = string.sub(message, bStart, bEnd)
			table.insert(batches, batch)
		end
		
		--Check and execute each batch
		for batchPos, msg in pairs(batches) do
			wait()
			
			--Split message into commandName and arguments
			local args = {}
			msg:gsub('([^'..settings.SplitKey..']+)',function(c) args[#args+1] = string.lower(c) end);
			local commandPrefix, commandName = string.sub(args[1],1,1), string.sub(args[1],2)
			table.remove(args,1)
			
			--Loop commands
			local loops = 1
			local finalArg = tonumber(args[#args])
			if string.sub(commandName,1,4) == "loop" then
				commandName = string.sub(commandName,5)
				if finalArg then
					loops = finalArg
				end
				if loops > 100 or not finalArg then
					loops = 100
				end
			end
			
			-- Check if alias and convert to commandName
			local originalAlias
			local commandNameFromAlias = main.infoOnAllCommands["Aliases"][commandName]
			if commandNameFromAlias then
				originalAlias = commandName
				commandName = string.lower(commandNameFromAlias)
			end
			
			--Check if UnFunction
			local unFunction = false
			if string.sub(commandName,1,2) == "un" then
				local unCommandName = string.sub(commandName,3)
				local unCommandNameFromAlias = main.infoOnAllCommands["Aliases"][unCommandName]
				if unCommandNameFromAlias then
					originalAlias = unCommandName
					unCommandName = string.lower(unCommandNameFromAlias)
				end
				local unCommand = main.commands[unCommandName]
				if unCommand and (type(unCommand.UnFunction) == "function" or unCommand.ClientCommand or unCommand.ClientCommandToActivate) then
					unFunction = true
					commandName = unCommandName
					loops = 1
				end
			end
			
			local errorNotice
			local command = main.commands[commandName]
			if not command then
				if main:GetModule("cf"):CheckRankExists(commandName) then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidCommandRank", commandName, settings.Prefix, commandName)
				elseif #commandName > 3 then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidCommandNormal", commandName)
				end
			else
				if not command.Loopable and loops > 1 then
					loops = 1
				end
				
				--Setup debounces
				local commandDebounceName = commandName
				if command.OriginalName then
					commandDebounceName = command.OriginalName
				end
				if main.functionsInLoop[commandDebounceName] == nil then
					main.functionsInLoop[commandDebounceName] = {}
				end
				
				--Check if using right prefix for the command
				local tryPrefix = speakerData.Prefix
				local tryName = commandName
				if command.Prefixes[1] == main.settings.UniversalPrefix then
					tryPrefix = main.settings.UniversalPrefix
				end
				if commandPrefix ~= speakerData.Prefix and commandPrefix ~= main.settings.UniversalPrefix then
					if originalAlias then
						tryName = originalAlias
					end
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidPrefix", tryPrefix, tryName)
				
				--Check if VIPServer and using permRank
				elseif game.VIPServerOwnerId ~= 0 and main:GetModule("cf"):FindValue(settings.VIPServerCommandBlacklist, command.Name) then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidVipServer", command.Name)
						
				--Check if Donor command and Speaker has Donor OR rank >= 4
				elseif command.Rank == "Donor" and (not speakerData.Donor or (commandPrefix ~= main.settings.UniversalPrefix and speakerData.Rank < 4)) then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidDonor")
					main.signals.ShowPage:FireClient(speaker, {"Special", "Donor"})
						
				--Check if Loop command and Speaker has permission to use
				elseif loops > 1 and speakerData.Rank < settings.LoopCommands then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidLoop")
				
				--Check if speaker has permission to use command
				elseif command.Rank ~= "Donor" and speakerData.Rank < command.Rank then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidRank", commandName)
				
				--Check for command block
				elseif not unFunction and main.commandBlocks[speaker] then
					errorNotice = main:GetModule("cf"):FormatNotice("ParserCommandBlock")
				
				--Command limit
				elseif main.commandsExecuted[speaker] and main.commandsExecuted[speaker] >= main.settings.CommandLimitPerMinute/main.commandsExecutedDivider and speakerData.Rank < main.settings.IgnoreCommandLimitPerMinute then
					errorNotice = main:GetModule("cf"):FormatNotice("CommandLimitPerMinute")
					main.signals.Error:FireClient(speaker, errorNotice)
					return
					
				else
					
					main.commandsExecuted[speaker] = main.commandsExecuted[speaker] or 0
					main.commandsExecuted[speaker] = main.commandsExecuted[speaker] + 1
								
					--Record in logs
					coroutine.wrap(function()
						local messageToRecord = commandName--msg
						if loops > 1 then
							messageToRecord = "loop"..messageToRecord
						end
						if unFunction then
							messageToRecord = "un"..messageToRecord
						end
						messageToRecord = tryPrefix..messageToRecord
						local restOfMessage = string.sub(msg, #messageToRecord+1)
						restOfMessage = main:GetModule("cf"):FilterBroadcast(restOfMessage, speaker)
						messageToRecord = messageToRecord.. restOfMessage
						table.insert(main.logs.command, {speakerName = speaker.Name, timeAdded = os.time(), message = messageToRecord})
					end)()
					
					--Process arguments (e.g. rank, color, text, etc)
					local commandArgs = command.Args
					local originalArgs = args
					local forceExit
					originalAlias = originalAlias or commandName
					args, forceExit = main:GetModule("Arguments"):Process(commandArgs, args, commandPrefix, command.Name, speaker, speakerData, originalMessage, originalAlias, batches, batchPos)
					if forceExit then
						return
					end
					
					--Decide how to execute the command (and on which plrs if applicable)
					if commandArgs[1] == "player" and (commandName ~= "directban" or not unFunction) then
						local targetPlayers = main:GetModule("Qualifiers"):GetTargetPlayers(speaker, args, originalArgs, commandPrefix, command.Prefixes, commandName)
						
						if command.Teleport then
							
							local individual
							if commandName == "bring" then
								individual = speaker
							else
								individual = main:GetModule("Qualifiers"):GetTargetPlayers(speaker, {args[1]}, originalArgs, commandPrefix, command.Prefixes, commandName, true)
							end
							local targetPlayersTable = {}
							for plr, _ in pairs(targetPlayers) do
								if plr ~= individual then
									table.insert(targetPlayersTable, plr)
								end
							end
							command.Function(speaker, {targetPlayersTable, individual})
							success = true
							
						else
							local plrChangeInfo = {
								Ranked = {};
								Unranked = {};
							}
							local totalPlrs = 0
							for plr, _ in pairs(targetPlayers) do
								totalPlrs = totalPlrs + 1
							end
							local plrsRemaining = 0
							
							for plr, _ in pairs(targetPlayers) do
								
								--Check Rig
								local humanoid = main:GetModule("cf"):GetHumanoid(plr)
								local rr = command.RequiresRig
								if humanoid and rr and humanoid.RigType ~= rr then
									local rrName = "R15"
									if rr == Enum.HumanoidRigType.R6 then
										rrName = "R6"
									end
									errorNotice = main:GetModule("cf"):FormatNotice("ParserInvalidRigType", plr.Name, rrName)
									
								--Check if punished
								elseif command.BlockWhenPunished and main:GetModule("cf"):IsPunished(plr) then
									errorNotice = main:GetModule("cf"):FormatNotice("ParserPlrPunished", plr.Name)
								
								else
									local plrData = main.pd[plr]
									
									-- Check for RankLock. If so, is plr's rank greater than speaker's?
									if not command.RankLock or (plrData and speakerData.Rank > plrData.Rank) then
										
										--Only the run command if not already running (and commandDebounce enabled)
										if not commandDebounce or not main.functionsInLoop[commandDebounceName][plr] then
											
											--Add in 'plr' argument to args
											local plrArgs = {plr}
											for i,v in pairs(args) do
												table.insert(plrArgs, v)
											end
											
											coroutine.wrap(function()
												main.functionsInLoop[commandDebounceName][plr] = 0
												plrsRemaining = plrsRemaining + 1
												for i = 1, loops do
													------------------------------------
													local other = {ExtraDetails = extraDetails, UnFunction = unFunction}
													local returnInfo = main:GetModule("cf"):ExecuteCommand(speaker, plrArgs, command, other)
													success = true
													------------------------------------
													if returnInfo == "Ranked" or returnInfo == "Unranked" then
														table.insert(plrChangeInfo[returnInfo], plr)
													end
													wait(0.1)
													local humanoid = main:GetModule("cf"):GetHumanoid(plr)
													if humanoid and humanoid.Health < 1 then					
														plr.CharacterAdded:Wait()
														wait(0.1)
													end
													if plr == nil or not main.functionsInLoop[commandDebounceName][plr] then
														break
													end
												end
												plrsRemaining = plrsRemaining - 1
												main.functionsInLoop[commandDebounceName][plr] = nil
											end)()
										end
										
									elseif totalPlrs <= 1 then
										if commandName == "directban" then
											commandName = "ban"
										end
										main:GetModule("cf"):FormatAndFireError(speaker, "ParserPlayerRankBlocked", commandName, plr.Name)
										
									end
								end
							end
							spawn(function()
								repeat wait(0.1) until plrsRemaining == 0
								if #plrChangeInfo.Ranked > 0 then
									local targetRankName = main:GetModule("cf"):GetRankName(args[1])
									local amount = "1 person"
									if #plrChangeInfo.Ranked > 1 then
										amount = #plrChangeInfo.Ranked.." people"
									end
									local notice = main:GetModule("cf"):FormatNotice("ParserSpeakerRank", command.Name, amount, targetRankName)
									main.signals.Notice:FireClient(speaker, notice)
								end	
								if #plrChangeInfo.Unranked > 0 then
									local amount = "1 person."
									if #plrChangeInfo.Unranked > 1 then
										amount = #plrChangeInfo.Unranked.." people."
									end
									local notice = main:GetModule("cf"):FormatNotice("ParserSpeakerUnrank", amount)
								end
								--main.playersRanked[speaker] = nil
								--main.playersUnranked[speaker] = nil
							end)
						end
						
					elseif not main.functionsInLoop[commandDebounceName][speaker] or unFunction then
						main.functionsInLoop[commandDebounceName][speaker] = true
						-------------------------------
						local other = {ExtraDetails = extraDetails, UnFunction = unFunction}
						local returnInfo = main:GetModule("cf"):ExecuteCommand(speaker, args, command, other)
						success = true
						-------------------------------
						main.functionsInLoop[commandDebounceName][speaker] = nil
						
					end
				
				end
				
			end
			
			--Speaker error message
			if errorNotice then
				main.signals.Error:FireClient(speaker, errorNotice)
			end
			
		end
	
	end
		
	return success	
end









return module
