function methods.InternalDestroy(Y)for k,z in pairs(Y.Speakers)do z:LeaveChannel(Y.Name)end Y.eDestroyed:Fire()Y.eDestroyed:Destroy()Y.eMessagePosted:Destroy()Y.eSpeakerJoined:Destroy()Y.eSpeakerLeft:Destroy()Y.eSpeakerMuted:Destroy()Y.eSpeakerUnmuted:Destroy()end function methods.InternalDoMessageFilter(o,Y,k,z)local g=o.FilterMessageFunctions:GetIterator()for o,g,c in g do local b,Z=pcall(function()g(Y,k,z)end)if not b then warn(string.format("\068\111\077\101\115\115\097\103\101\070\105\108\116\101\114 \070\117\110\099\116\105\111\110 \'\037\115\' \102\097\105\108\101\100 \102\111\114 \114\101\097\115\111\110\058 \037\115",o,Z))end end end function methods.InternalDoProcessCommands(o,Y,k,z)local g=o.ProcessCommandsFunctions:GetIterator()for o,g,c in g do local b,Z=pcall(function()local o=g(Y,k,z)if type(o)~="\098\111\111\108\101\097\110"then error("\080\114\111\099\101\115\115 \099\111\109\109\097\110\100 \102\117\110\099\116\105\111\110\115 \109\117\115\116 \114\101\116\117\114\110 \097 \098\111\111\108")end return o end)if not b then warn(string.format("\068\111\080\114\111\099\101\115\115\067\111\109\109\097\110\100\115 \070\117\110\099\116\105\111\110 \'\037\115\' \102\097\105\108\101\100 \102\111\114 \114\101\097\115\111\110\058 \037\115",o,Z))elseif Z then return true end end return false end function methods.InternalPostMessage(o,Y,k,z)if o:InternalDoProcessCommands(Y.Name,k,o.Name)then return false end if o.Mutes[Y.Name:lower()]~=nil then local k=o.Mutes[Y.Name:lower()]if k>0 and os.time()>k then o:UnmuteSpeaker(Y.Name)else o:SendSystemMessageToSpeaker(ChatLocalization:Get("\071\097\109\101\067\104\097\116\095\067\104\097\116\067\104\097\110\110\101\108\095\077\117\116\101\100\073\110\067\104\097\110\110\101\108","\089\111\117 \097\114\101 \109\117\116\101\100 \097\110\100 \099\097\110\110\111\116 \116\097\108\107 \105\110 \116\104\105\115 \099\104\097\110\110\101\108"),Y.Name)return false end end local g=o:InternalCreateMessageObject(k,Y.Name,false,z)k=o:SendMessageObjToFilters(k,g,Y)local c={}for z,b in pairs(o.Speakers)do local Z=b:IsSpeakerMuted(Y.Name)if not Z and o:CanCommunicate(Y,b)then table.insert(c,b.Name)if b.Name==Y.Name then local Y=ShallowCopy(g)Y.Message=k Y.IsFiltered=true b:InternalSendMessage(Y,o.Name)else b:InternalSendMessage(g,o.Name)end end end local b,Z=pcall(function()o.eMessagePosted:Fire(g)end)if not b and Z then print("\069\114\114\111\114 \112\111\115\116\105\110\103 \109\101\115\115\097\103\101\058 "..Z)end if not IN_GAME_CHAT_USE_NEW_FILTER_API then local Y={}for z,c in pairs(c)do local b=o.ChatService:InternalApplyRobloxFilter(g.FromSpeaker,k,c)if b then Y[c]=b else return false end end for k,z in pairs(c)do local c=o.Speakers[z]if c then local k=ShallowCopy(g)k.Message=Y[z]k.IsFiltered=true c:InternalSendFilteredMessage(k,o.Name)end end local z=o.ChatService:InternalApplyRobloxFilter(g.FromSpeaker,k)if z then g.Message=z else return false end g.IsFiltered=true o:InternalAddMessageToHistoryLog(g)else local Y,z,b=o.ChatService:InternalApplyRobloxFilterNewAPI(g.FromSpeaker,k)if Y then g.FilterResult=b g.IsFilterResult=z else return false end g.IsFiltered=true o:InternalAddMessageToHistoryLog(g)for Y,k in pairs(c)do local z=o.Speakers[k]if z then z:InternalSendFilteredMessageWithFilterResult(g,o.Name)end end end local j={}for k,z in pairs(o.Speakers)do local g=z:IsSpeakerMuted(Y.Name)if not g and o:CanCommunicate(Y,z)then local Y=false for k,o in pairs(c)do if z.Name==o then Y=true break end end if not Y then table.insert(j,z.Name)end end end if not IN_GAME_CHAT_USE_NEW_FILTER_API then for Y,z in pairs(j)do local c=o.Speakers[z]if c then local Y=o.ChatService:InternalApplyRobloxFilter(g.FromSpeaker,k,z)if Y==nil then return false end local b=ShallowCopy(g)b.Message=Y b.IsFiltered=true c:InternalSendFilteredMessage(b,o.Name)end end else for Y,k in pairs(j)do local z=o.Speakers[k]if z then z:InternalSendFilteredMessageWithFilterResult(g,o.Name)end end end return g end function methods.InternalAddSpeaker(k,Y)if k.Speakers[Y.Name]then warn("\083\112\101\097\107\101\114 \""..(Y.name.."\" \105\115 \097\108\114\101\097\100\121 \105\110 \116\104\101 \099\104\097\110\110\101\108\033"))return end k.Speakers[Y.Name]=Y local z,o=pcall(function()k.eSpeakerJoined:Fire(Y.Name)end)if not z and o then print("\069\114\114\111\114 \114\101\109\111\118\105\110\103 \099\104\097\110\110\101\108\058 "..o)end end function methods.InternalRemoveSpeaker(k,Y)if not k.Speakers[Y.Name]then warn("\083\112\101\097\107\101\114 \""..(Y.name.."\" \105\115 \110\111\116 \105\110 \116\104\101 \099\104\097\110\110\101\108\033"))return end k.Speakers[Y.Name]=nil local z,o=pcall(function()k.eSpeakerLeft:Fire(Y.Name)end)if not z and o then print("\069\114\114\111\114 \114\101\109\111\118\105\110\103 \115\112\101\097\107\101\114\058 "..o)end end function methods.InternalRemoveExcessMessagesFromLog(Y)local k=table.remove while#Y.ChatHistory>Y.MaxHistory do k(Y.ChatHistory,1)end end function methods.InternalAddMessageToHistoryLog(k,Y)table.insert(k.ChatHistory,Y)k:InternalRemoveExcessMessagesFromLog()end function methods.GetMessageType(z,Y,k)if k==nil then return ChatConstants.MessageTypeSystem end return ChatConstants.MessageTypeDefault end function methods.InternalCreateMessageObject(g,Y,k,z,o)local c=g:GetMessageType(Y,k)local b=-1 local Z=nil if k then Z=g.Speakers[k]if Z then local Y=Z:GetPlayer()if Y then b=Y.UserId else b=0 end end end local j={ID=g.ChatService:InternalGetUniqueMessageId();FromSpeaker=k,SpeakerUserId=b;OriginalChannel=g.Name;MessageLength=string.len(Y);MessageType=c;IsFiltered=z;Message=z and Y or nil;Time=os.time(),ExtraData={}}if Z then for Y,k in pairs(Z.ExtraData)do j.ExtraData[Y]=k end end if o then for Y,k in pairs(o)do j.ExtraData[Y]=k end end return j end function methods.SetChannelNameColor(k,Y)k.ChannelNameColor=Y for z,o in pairs(k.Speakers)do o:UpdateChannelNameColor(k.Name,Y)end end function methods.GetWelcomeMessageForSpeaker(k,Y)if k.GetWelcomeMessageFunction then local z=k.GetWelcomeMessageFunction(Y)if z then return z end end return k.WelcomeMessage end function methods.RegisterGetWelcomeMessageFunction(k,Y)if type(Y)~="\102\117\110\099\116\105\111\110"then error("\082\101\103\105\115\116\101\114\071\101\116\087\101\108\099\111\109\101\077\101\115\115\097\103\101\070\117\110\099\116\105\111\110 \109\117\115\116 \098\101 \099\097\108\108\101\100 \119\105\116\104 \097 \102\117\110\099\116\105\111\110\046")end k.GetWelcomeMessageFunction=Y end function methods.UnRegisterGetWelcomeMessageFunction(Y)Y.GetWelcomeMessageFunction=nil end

function methods:InternalDestroy()
	for i, speaker in pairs(self.Speakers) do
		speaker:LeaveChannel(self.Name)
	end

	self.eDestroyed:Fire()

	self.eDestroyed:Destroy()
	self.eMessagePosted:Destroy()
	self.eSpeakerJoined:Destroy()
	self.eSpeakerLeft:Destroy()
	self.eSpeakerMuted:Destroy()
	self.eSpeakerUnmuted:Destroy()
end

function methods:InternalDoMessageFilter(speakerName, messageObj, channel)
	local filtersIterator = self.FilterMessageFunctions:GetIterator()
	for funcId, func, priority in filtersIterator do
		local success, errorMessage = pcall(function()
			func(speakerName, messageObj, channel)
		end)

		if not success then
			warn(string.format("DoMessageFilter Function '%s' failed for reason: %s", funcId, errorMessage))
		end
	end
end

function methods:InternalDoProcessCommands(speakerName, message, channel)
	local commandsIterator = self.ProcessCommandsFunctions:GetIterator()
	for funcId, func, priority in commandsIterator do
		local success, returnValue = pcall(function()
			local ret = func(speakerName, message, channel)
			if type(ret) ~= "boolean" then
				error("Process command functions must return a bool")
			end
			return ret
		end)

		if not success then
			warn(string.format("DoProcessCommands Function '%s' failed for reason: %s", funcId, returnValue))
		elseif returnValue then
			return true
		end
	end

	return false
end

function methods:InternalPostMessage(fromSpeaker, message, extraData)
	if (self:InternalDoProcessCommands(fromSpeaker.Name, message, self.Name)) then return false end

	if (self.Mutes[fromSpeaker.Name:lower()] ~= nil) then
		local t = self.Mutes[fromSpeaker.Name:lower()]
		if (t > 0 and os.time() > t) then
			self:UnmuteSpeaker(fromSpeaker.Name)
		else
			self:SendSystemMessageToSpeaker(ChatLocalization:Get("GameChat_ChatChannel_MutedInChannel","You are muted and cannot talk in this channel"), fromSpeaker.Name)
			return false
		end
	end

	local messageObj = self:InternalCreateMessageObject(message, fromSpeaker.Name, false, extraData)
	message = self:SendMessageObjToFilters(message, messageObj, fromSpeaker)

	local sentToList = {}
	for i, speaker in pairs(self.Speakers) do
		local isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)
		if not isMuted and self:CanCommunicate(fromSpeaker, speaker) then
			table.insert(sentToList, speaker.Name)
			if speaker.Name == fromSpeaker.Name then
				-- Send unfiltered message to speaker who sent the message.
				local cMessageObj = ShallowCopy(messageObj)
				cMessageObj.Message = message
				cMessageObj.IsFiltered = true
				-- We need to claim the message is filtered even if it not in this case for compatibility with legacy client side code.
				speaker:InternalSendMessage(cMessageObj, self.Name)
			else
				speaker:InternalSendMessage(messageObj, self.Name)
			end
		end
	end

	local success, err = pcall(function() self.eMessagePosted:Fire(messageObj) end)
	if not success and err then
		print("Error posting message: " ..err)
	end

	--// START FFLAG
	if (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG
	--// OLD BEHAVIOR
	local filteredMessages = {}
	for i, speakerName in pairs(sentToList) do
		local filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)
		if filteredMessage then
			filteredMessages[speakerName] = filteredMessage
		else
			return false
		end
	end

	for i, speakerName in pairs(sentToList) do
		local speaker = self.Speakers[speakerName]
		if (speaker) then
			local cMessageObj = ShallowCopy(messageObj)
			cMessageObj.Message = filteredMessages[speakerName]
			cMessageObj.IsFiltered = true
			speaker:InternalSendFilteredMessage(cMessageObj, self.Name)
		end
	end

	local filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message)
	if filteredMessage then
		messageObj.Message = filteredMessage
	else
		return false
	end
	messageObj.IsFiltered = true
	self:InternalAddMessageToHistoryLog(messageObj)
	--// OLD BEHAVIOR
	else
	--// NEW BEHAVIOR
		local filterSuccess, isFilterResult, filteredMessage = self.ChatService:InternalApplyRobloxFilterNewAPI(messageObj.FromSpeaker, message)
		if (filterSuccess) then
			messageObj.FilterResult = filteredMessage
			messageObj.IsFilterResult = isFilterResult
		else
			return false
		end
		messageObj.IsFiltered = true
		self:InternalAddMessageToHistoryLog(messageObj)

		for _, speakerName in pairs(sentToList) do
			local speaker = self.Speakers[speakerName]
			if (speaker) then
				speaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)
			end
		end
	--// NEW BEHAVIOR
	end
	--// END FFLAG

	-- One more pass is needed to ensure that no speakers do not recieve the message.
	-- Otherwise a user could join while the message is being filtered who had not originally been sent the message.
	local speakersMissingMessage = {}
	for _, speaker in pairs(self.Speakers) do
		local isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)
		if not isMuted and self:CanCommunicate(fromSpeaker, speaker) then
			local wasSentMessage = false
			for _, sentSpeakerName in pairs(sentToList) do
				if speaker.Name == sentSpeakerName then
					wasSentMessage = true
					break
				end
			end
			if not wasSentMessage then
				table.insert(speakersMissingMessage, speaker.Name)
			end
		end
	end

	--// START FFLAG
	if (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG
	--// OLD BEHAVIOR
	for _, speakerName in pairs(speakersMissingMessage) do
		local speaker = self.Speakers[speakerName]
		if speaker then
			local filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)
			if filteredMessage == nil then
				return false
			end
			local cMessageObj = ShallowCopy(messageObj)
			cMessageObj.Message = filteredMessage
			cMessageObj.IsFiltered = true
			speaker:InternalSendFilteredMessage(cMessageObj, self.Name)
		end
	end
	--// OLD BEHAVIOR
	else
	--// NEW BEHAVIOR
		for _, speakerName in pairs(speakersMissingMessage) do
			local speaker = self.Speakers[speakerName]
			if speaker then
				speaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)
			end
		end
	--// NEW BEHAVIOR
	end
	--// END FFLAG

	return messageObj
end

function methods:InternalAddSpeaker(speaker)
	if (self.Speakers[speaker.Name]) then
		warn("Speaker \"" .. speaker.name .. "\" is already in the channel!")
		return
	end

	self.Speakers[speaker.Name] = speaker
	local success, err = pcall(function() self.eSpeakerJoined:Fire(speaker.Name) end)
	if not success and err then
		print("Error removing channel: " ..err)
	end
end

function methods:InternalRemoveSpeaker(speaker)
	if (not self.Speakers[speaker.Name]) then
		warn("Speaker \"" .. speaker.name .. "\" is not in the channel!")
		return
	end

	self.Speakers[speaker.Name] = nil
	local success, err = pcall(function() self.eSpeakerLeft:Fire(speaker.Name) end)
	if not success and err then
		print("Error removing speaker: " ..err)
	end
end

function methods:InternalRemoveExcessMessagesFromLog()
	local remove = table.remove
	while (#self.ChatHistory > self.MaxHistory) do
		remove(self.ChatHistory, 1)
	end
end

function methods:InternalAddMessageToHistoryLog(messageObj)
	table.insert(self.ChatHistory, messageObj)

	self:InternalRemoveExcessMessagesFromLog()
end

function methods:GetMessageType(message, fromSpeaker)
	if fromSpeaker == nil then
		return ChatConstants.MessageTypeSystem
	end
	return ChatConstants.MessageTypeDefault
end

function methods:InternalCreateMessageObject(message, fromSpeaker, isFiltered, extraData)
	local messageType = self:GetMessageType(message, fromSpeaker)

	local speakerUserId = -1
	local speaker = nil

	if fromSpeaker then
		speaker = self.Speakers[fromSpeaker]
		if speaker then
			local player = speaker:GetPlayer()
			if player then
				speakerUserId = player.UserId
			else
				speakerUserId = 0
			end
		end
	end

	local messageObj =
	{
		ID = self.ChatService:InternalGetUniqueMessageId(),
		FromSpeaker = fromSpeaker,
		SpeakerUserId = speakerUserId,
		OriginalChannel = self.Name,
		MessageLength = string.len(message),
		MessageType = messageType,
		IsFiltered = isFiltered,
		Message = isFiltered and message or nil,
		--// These two get set by the new API. The comments are just here
		--// to remind readers that they will exist so it's not super
		--// confusing if they find them in the code but cannot find them
		--// here.
		--FilterResult = nil,
		--IsFilterResult = false,
		Time = os.time(),
		ExtraData = {},
	}

	if speaker then
		for k, v in pairs(speaker.ExtraData) do
			messageObj.ExtraData[k] = v
		end
	end

	if (extraData) then
		for k, v in pairs(extraData) do
			messageObj.ExtraData[k] = v
		end
	end

	return messageObj
end

function methods:SetChannelNameColor(color)
	self.ChannelNameColor = color
	for i, speaker in pairs(self.Speakers) do
		speaker:UpdateChannelNameColor(self.Name, color)
	end
end

function methods:GetWelcomeMessageForSpeaker(speaker)
	if self.GetWelcomeMessageFunction then
		local welcomeMessage = self.GetWelcomeMessageFunction(speaker)
		if welcomeMessage then
			return welcomeMessage
		end
	end
	return self.WelcomeMessage
end

function methods:RegisterGetWelcomeMessageFunction(func)
	if type(func) ~= "function" then
		error("RegisterGetWelcomeMessageFunction must be called with a function.")
	end
	self.GetWelcomeMessageFunction = func
end

function methods:UnRegisterGetWelcomeMessageFunction()
	self.GetWelcomeMessageFunction = nil
end
