How would I implement filtering into my server script for my custom chat?

So I’ve made a custom chat GUI which works as expected. I now need to filter the actual text, however I’ve been stuck for 2 days on how to implement it into my script since all the example scripts look different to mine. I would really appreciate it if someone could explain how I would implement it into my code specifically.
Below is my server script which basically sends the text to everyone.

local event = game:GetService("ReplicatedStorage"):WaitForChild("ChatEvent")

event.OnServerEvent:Connect(function(plr, message)
	if message ~= "" then
		event:FireAllClients(message, plr.Name)
	end
end)
2 Likes

use game:GetService(“TextService”), and then instead of event:FireAllClients(message, plr.Name), you can do event:FireAllClients(TextService:FilterStringAsync(message), plr.Name). In each individual local script you can do message:GetChatForUserAsync(local player’s UserID) to make sure the chat is properly filtered for them.

My server code now looks like this:

event = game:GetService("ReplicatedStorage"):WaitForChild("ChatEvent")
TextService = game:GetService("TextService")

event.OnServerEvent:Connect(function(plr, message)
	if message ~= "" then
		event:FireAllClients(TextService:FilterStringAsync(message), plr.Name)
	end
end)

My local script now looks like this:

script.Parent.Parent.Enabled = true

--Chat Module--
local textbox = script.Parent.Chat.Send.TextBox
local holder = script.Parent.Chat.Holder
local event = game:GetService("ReplicatedStorage"):WaitForChild("ChatEvent")
local template = game:GetService("ReplicatedStorage"):WaitForChild("temp")
local BottomRInfo = script.Parent.BottomRInfo
local Time = BottomRInfo.Time
local lighting = game:GetService("Lighting")
local player = game.Players.LocalPlayer

textbox.FocusLost:Connect(function(ep)
	if (ep) and textbox.Text ~= "" then
		event:FireServer(textbox.Text)
		textbox.Text = ""
	else
		warn("Please Enter Text")
	end
end)

event.OnClientEvent:Connect(function(message, plrThatSent)
	local clone = template:Clone()
	clone.plrName.Text = "["..plrThatSent.."]:"
	clone.message.Text = message:GetChatForUserAsync(player.UserId)
	clone.Parent = script.Parent.Chat.Holder
end)

Although I’m now getting this error:
image

Line 16 = event:FireAllClients(TextService:FilterStringAsync(message), plr.Name)

yes, my bad. FilterStringAsync takes 3 arguments, I thought that the 2nd and 3rd were optional, but turns out that the 2nd is mandatory. It should be “The userId of the player filtering the text” according to the dev hub (so you can just do plr.UserId on the server script).

1 Like

I got the same error again :frowning:

For a chat system like this your best chance is the Chat:FilterStringForBroadcast. What @Chatowillwin mentioned will work except it is intended more for direct messages to other player’s and if you wanted to use the TextService’s filter you would need to make a seperate filtered message for each player on the server.

yeah but how would I implement that

You really only need to edit the code in your server script. Inside your if statement replace it with

event:FireAllClients(ChatService:FilterStringForBroadcast(message, plr))

Roblox’s 2 methods to filter text are very similar the only diffrence is if their is a 2nd player argument. This is useful for when you are making direct messages between players when the chat filter can adapt between the <13 or 13+ chat filter.

heh, chat filtering, my name sake. Ok so…

To implement this, you should be using the TextService:FilterStringAsync(message, userID, context) function.

You must implement this correctly, so for sending global messages in chat, you should be using the PublicChat TextFilterContext enum.

When you have successfully filtered a message, you’ll get a object called TextFilterResult
You should then send the TextFilterResult:GetChatForUserAsync(userID) function to each client respectively. (You also have to filter "" as a fail safe, roblox is weird ik)

A possible implementation for this could be

ReplicatedStorage.PlayerChatted.OnServerEvent:Connect(p, message)
   --FilterStringAsync can fail so we'll need to wrap it in a pcall
   local worked, filterResult = pcall(TextService.FilterStringAsync, TextService, message, p.UserId, Enum.TextFilterContext.PublicChat) --pcall adds error handling, pretty much required for HTTP calls
   
   if not worked then
      --find some way to handle it not being filtered, you CANNOT return the message unfiltered
      return --ends the function early returning nothing
   else
      for _, v in pairs(Players:GetPlayers()) do --runs the code in this block for every player as 'v'
        ReplicatedStorage.SendFilteredMessage:FireClient(v, filterResult:GetChatForUserAsync(v.UserId), p.Name) --this can yield but shouldn't in most usecases, so we'll let it run synchronously here
      end
   end
end)

Wait what’s going on with Line 3

Table:Function() is actually just syntax sugar for Table.Function(Table), they’ll behave exactly the same way. pcall will always call a function with ., so we need to parse the TextService itself aswell. It will work exactly the same way and will not hinder performance.

Other Notes:
I only told how to do filtering here, you also need to factor Chat:CanUserChat() for users which have the setting off in privacy settings.

You should just stick the default ChatService, once you open it’s API, it’s very powerful, and you can probably make your own GUIs for it with enough work.

2 Likes

Thanks for bringing this up I actually never considered this aspect to be considered.

Wait so I could’ve just skipped all this coding by just modifying an API with my own GUI?
Where can I find this chat API?