A Guide to Filtering Text

What is filtering?

Filtering is the way of checking text that has been passed through from a client to ensure that it is clean, and it stays within the guidelines of Roblox’s filtering system.

Filtering is super important because it protects naive people from sharing personal information, and protects them from vulgar language.

An oversimplified way this can be displayed is like this:

On the Developer Hub, there is another more indepth model that displays this.

How do I filter text?

The filtering system relies on a service called TextService. Also, this all needs to happen on the server, so make sure not to do it on the client to avoid any nasty errors.

The service can be added simply by retrieving the service: game:GetService("TextService")
Filtering is done by using the function: FilterStringAsync(), which as you may have guessed, is a function of the service we just retrieved, TextService.

FilterStringAsync has three parameters:

  • the string to filter
  • the UserId that the text has come from
  • the context of the message (e.g. global, private messages)

Example 1
Let’s say that when a player presses a TextButton, you want to filter that text that they inputted through the TextBox. This could be used for a custom chat, nickname, pet naming, etc. All valid reasons to filter text.

  1. To set the environment up, we’ll need an event to fire the event to the server and then back to the clients once it’s been filtered, a LocalScript and a Script.
  2. Design a simple UI, with a TextBox, TextButton and a TextLabel.
  3. In your LocalScript, you’ll need to define your variables at the top, including the event. Then, add an event to see when a player clicks the button that it sends the text in the TextBox to the server.

It can be as basic as this:

--// Variables
local Event = game:GetService("ReplicatedStorage"):WaitForChild("FilterMessage")

local Input = script.Parent:WaitForChild("Frame"):WaitForChild("InputBox")
local Submit = script.Parent:WaitForChild("Frame"):WaitForChild("Button")

local Display = script.Parent:WaitForChild("Frame"):WaitForChild("TextLabel")


--// When the button is clicked
local function buttonClicked()
	if Input.Text ~= "" then
		Event:FireServer(Input.Text)
	end
end

--// Bind the event to the function
Submit.MouseButton1Down:Connect(buttonClicked)
  1. That’s the LocalScript done for now. Go to the Script that you created earlier.
    This time, we’ll be using the TextService module that we talked about earlier.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local TextService = game:GetService("TextService")
    local Event = ReplicatedStorage:WaitForChild("FilterMessage")

    local function filterMessage(msg, fromUser)
    	local result
    	local success, err = pcall(function()
    		result = TextService:FilterStringAsync(msg, fromUser)
    	end)
    	if success then
    		return result
    	end
    	return false
    end

The function filterMessage() will be the function that we use to filter the message.

PLEASE REMEMBER: We always need to pcall() these as the filtering may fail on Roblox’s end. If you don’t know what pcall() is, it’s a way of “safely erroring” if something does go wrong. If it errors, the Script can still run.

The “result” variable will be what we store the filtered text in.
TextService:FilterStringAsync(msg, fromUser). Message is the text that you typed in the TextBox, and fromUser is the player it came from.

  1. This function will prepare the final text for the player, based on their age (safechat or not). For example, if you typed 234837471239 into the TextBox, you’d see it when it’s displayed at the end of this tutorial. However, if you had safechat, you wouldn’t see it when it’s displayed later. It’ll make sense later when you test it out.
local function getFilteredMessage(text, recipient)
	local result
	local success, err = pcall(function()
		result = text:GetChatForUserAsync(recipient)
	end)
	if success then
		return result
	end
	return false
end

Again, we’re using pcall() to make sure that we can safely run the script. And then if it runs correctly, we return the finally filtered text under the local scope variable “result”.

  1. This part sends the message to the player when the event has been fired. All the functions that we completed above will be used in this function.
local function onSendMessage(sender, message)
	if message ~= "" then
		local filteredMessage = filterMessage(message, sender.UserId)
 
		if filteredMessage then
			for _, player in pairs(game.Players:GetPlayers()) do
				local filteredMessage = getFilteredMessage(filteredMessage, player.UserId)
				Event:FireClient(player, filteredMessage)
			end
		end
	end
end

Event.OnServerEvent:Connect(onSendMessage)

First, the function checks if the message actually exists (the textbox wasn’t left blank). The next line then uses the first function and the two parameters local function filterMessage(msg, fromUser) and the returned value is set to the “filteredMessage” variable.

If there was no errors, it then loops through all players in the game, and uses the second function that we created earlier, with the two parameters: local function getFilteredMessage(text, recipient). This is then fired to the client. We have to do this individually, opposed to doing FireAllClients(), as not everyone has the same chat restrictions.

  1. Go back to your LocalScript to receive the FireClient() event fired from your Script.
local function onClientReceive(message)
	Display.Text = message
end

And then add the bind that connects the event to that function.
Event.OnClientEvent:Connect(onClientReceive)

The end result should be something like this:
image
This text is filitered as people could reveal personal information (pretty obvious, but if you didn’t understand, hopefully you do now)

When do I know when to filter text?

It’ll be fairly obvious, don’t worry. If you lack control over something (e.g. someone can name their pet, custom nickname, etc). Any input from the player should be filtered, just to be on the safe side. Rarely, when generating random words, it may generate a word that Roblox’s filter does not like. However, if you don’t filter it, then you’ll be subject to moderation. Stored text and text from external third party sources outside of Roblox need to be filtered too.

Good things to know

  • Always pcall() your text filtering as there is chance that it may fail.
  • Filtering won’t work in Studio by default, so if you publish it and then test it in game, it will work.
  • A game without proper filtering will be taken down by Roblox, and your account may be subject to moderation.

Important Links

TextFilteringTutorial.rbxl (26.3 KB)

https://developer.roblox.com/en-us/api-reference/class/TextService
https://developer.roblox.com/en-us/api-reference/function/TextFilterResult/GetChatForUserAsync

Ending

Thanks for reading this, I hope it helped you. This was my first tutorial, any feedback is appreciated.
fin

89 Likes

Very nice tutorial on the whole, however one thing I would have liked this tutorial to have covered is the ethics vs the advantages of allowing players to bypass the filters, with my example being allowing players to make and place signs, since you could put two unfiltered signs next to each other to make sign showing text that should have been filtered.

Now the obvious ethical answer to this question is to excessively filter strings and try to look for any ways the player could have bypassed the filter, however this also has it’s downsides, being it can be inefficient and negatively affect (in terms of performance) every player not bypassing the filter, be very time consuming to make excessive filtering systems, in addition to the fact trollers bring a lot of money to games and therefor restricting them from trolling may negatively impact the game itself. This issue cannot only be applied to signs but also many times in games where you can freely build i’ve seen people abuse the system with developers making no attempt to stop them.

So my question is, is it ethically right to make no attempt to stop people from bypassing filters? or should we try to stop them at every turn? Furthermore can roblox terminate developers for not doing this ‘excessive filtering’?

6 Likes

I saw in a post recently that a developer had stored unfiltered data to be passed to external webhooks. Even though they weren’t shown to any players, Roblox still warned him. I get your point about revenue and unnecessary filtering but if it’s worth filtering, then do it. I haven’t seen a game that has pretty much explicitly “encouraged” that behaviour, but that’s not to say that it doesn’t exist. To save time, you could have a function to quickly filter things, surely?

Also, thanks for the feedback. Will work on adding the signs later. :slight_smile:

3 Likes

Sure with signs that is possible, but when it comes to freestyle building games I find making quick efficient function to filter is an entirely different level of task, maybe it would be accomplishable with Deep learning AI, but writing a function to detect if a block arrangement is taking the form of a filter word I would imagine is virtually impossible. So my question is more do we try? or do we just leave it?

2 Likes

Oh I see. That’s harder to detect. A report option would be an alternative but it’s pretty limited without spending loads of time on it.

3 Likes

Very helpful my guy. Thanks for this it helped me a lot with chat filter, an issue I had problems with.

Thanks for the resource I may use this myself.

Good tutorial, Now I just gotta link this to a warning command.

2 Likes

thank you for the tutorial, it’s very helpful!

1 Like

Thank you very much for this in-depth tutorial,

tip

I have to say this doesn’t seem to work inside Roblox studio, but it does inside the actual game server

thank you so much, its really helpful

Nice tutorial, I would like to say that the Box you made where it checks if the Text is cleans acts as the Server. So basically when the client chats or talks it is sent to the server. The server checks whether the text should be filtered. If the text needs to be filtered the server fires all clients with the filtered text. Otherwise, the server sends the original text of the sender.