This is episode one of a tutorial series I am creating! This is my first time doing something like this so please provide additional suggestions and feedback on this tutorial.
Fonts used:
Coolvetica
Cascadia Mono
Find the official Roblox Documentation for Text Filtering here
All important things are highlighed in blue. You can click blue text to find the official Documentation about something.
Chapter 1: Why & When?
Text filtering should always be used when user input is sent to other users. In easier words: You should filter text that a player has typed in. You shouldn’t filter things that the server is posting, such as any automatic posts, like timed chat messages and status messages. Only filter things uploaded by the client (later referred to as “Player” or/and “User”)! If you don’t filter things, you could be punished for letting users send/show/post inappropriate content, in most cases, curse words or phone numbers.
Chapter 2: How?
When I first tried to filter stuff, it came across as confusing, lot’s of functions and confusing stuff for a beginner. We’re gonna go over everything - and hopefully by the end you will understand most of it.
Let’s start with going over TextService
, the home of text filtering (And getting textsize… but no one really cares about that…). ANYWAY! There isn’t much to talk about the actual service, it’s not all that interesting apart from that it does text filtering and moderating. Okay, let’s move on.
Let’s talk about FilterStringAsync
, firstly lets learn what Async is, it’s short for asynchronous, meaning “controlling the timing of operations by the use of pulses sent when the previous operation is completed rather than at regular intervals.”, uhm, what? It basically just freezes a script until its finished. Read more confusing stuff about async here. Now, let’s get coding it!
FilterStringAsync
takes 2 required arguments, and 1 optional argument, making 3 arguments! Woo basic maths!! Okay seriously now. These arguments go as the following:
stringToFilter
: String
fromUserId
: Integer
textContext
: Enum
And this function, FilterStringAsync
returns one object… an instance (TextFilterResult
)!!
what?? a instance isn’t a string!! im not dumb?!?
Thanks captain obvious, Roblox is a bit picky so they want to know why it got filtered and who is filtering it… i dont really know why but i guess its for punishing people?
Alright then, let’s turn this instance into a string, it’s a bit more complex than it sounds, but in practise its really easy: We have 3 functions we can use on this instance:
GetChatForUserAsync
GetNonChatStringForBroadcastAsync
GetNonChatStringForUserAsync
All of these take the same or non arguments, so we don’t need to go over that. In this tutorial I’ll be using GetNonChatStringForUserAsync
, which requires a UserID argument. The other two work the exact same as this, however the GetNonChatStringForBroadcastAsync
doesn’t require a UserId.
When do I use GetChatForUserAsync?
The GetChatForUserAsync function returns the text in a properly filtered manner for the specified Player.UserId
. This should be used in the context of chats between players, although there are some other cases where text filtering is required.
This function returns the string appropriate for sending and displaying to a target user (specified by toUserId ) from the original sender using the least restrictive filtering appropriate for the target user, with Chat
privacy settings of both users enforced. This string should only be shown to the target user, as it might not be appropriate for all users.
This method throws an error if the two users are not allowed to chat (that is, if Chat:CanUserChatAsync
would return false for the given sender and receiver). If this method throws the string should not be displayed to the user. In addition, this function will throw an error if CanUserChatAsync would return false, so CanUserChatAsync should be called first to check.
This function currently throws an error if the user with the id toUserId is not online on the current server.
If text can be used for real-time or near real-time communication it should use this method.
This function will return immediately in most cases. The only time it will yield is if the target user is offline or has just joined the server and their filtering info is not yet loaded.
When do I use GetNonChatStringForBroadcastAsync?
Returns the text in a properly filtered manner for all users: Basically use it whenever something is being shown to all players.
When do I use GetNonChatStringForUserAsync?
Returns the text in a properly filtered manner for the specified Player.UserId
. This should be used in the context of non-chat text that another user can see, such as the name of a pet.
Cool, now you know which one to use, hopefully. Let’s show some example code to get you going.
Here’s an example function to filter some text for a player:
local function filterAsync(text, player)
local filteredTextResult
local success, errorMessage = pcall(function()
filteredTextResult = TextService:FilterStringAsync(text, player.UserId)
end)
if not success then
warn("Error filtering text:", text, ":", errorMessage)
return false, "Error while filtering text"
end
repeat task.wait() until filteredTextResult
return true, filteredTextResult:GetNonChatStringForUserAsync(player.UserId)
end
Now we shall go through significant each section., describing what it does.
Section 1
local filteredTextResult
local success, errorMessage = pcall(function()
filteredTextResult = TextService:FilterStringAsync(text, player.UserId)
end)
First we define filteredTextResult
as nothing, so we can set it later on, in our case, the next line. We then need to run the FilterStringAsync
function we mentioned earlier, to get our TextFilterResult
!. We need to wrap this in a pcall
, because TextService:FilterStringAsync
makes a webrequest, and sometimes they fail (dumb servers!!), so incase it fails, the pcall will protect it and not break everything. Like a shield!
Section 2
if not success then
warn("Error filtering text:", text, ":", errorMessage)
return false, "Error while filtering text"
end
The first line makes grammatical sense, if not success, then… basically if it fails then we’ll run the next piece of code. The next code basically just sends a output warning saying that it failed, along with some error information. Then we return false and the error so we know what happened later.
Section 3
repeat task.wait() until filteredTextResult
return true, filteredTextResult:GetNonChatStringForUserAsync(player.UserId)
The first line might not be english to you, but it simply waits until Roblox has given us the result, in our case the filteredTextResult
.
Then finally we return true meaning that it worked, along with the actual string (text), which we get by running filteredTextResult:GetNonChatStringForUserAsync(player.UserId)
.
Section 4 | Extra
Okay, we’ve got the function right, now lets use the function. Probably the easiest part.
First call it with 2 variables, result
and str
.
Let’s test it with some… bad words.
filterAsync("Hey, my phone number is 14581-457180-557271!", RudeBob)
RudeBob is breaking ToS! Let’s hope our function has stopped RudeBob from exposing such information.
Haha! Take that, RudeBob!
Disclaimer: RudeBob isn’t real, and you will need to specify an actual player object for this function.
Chapter 3: The End
Thanks for reading all the way! I hope you learned a thing or two. This is my first ever long tutorial, so I hope I explained things well. I will be making more tutorials in the future I hope!
If you have any criticism, feedback, or suggestions, please tell me!
Extras:
All Code
local function filterAsync(text, player)
local filteredTextResult
local success, errorMessage = pcall(function()
filteredTextResult = TextService:FilterStringAsync(text, player.UserId)
end)
if not success then
warn("Error filtering text:", text, ":", errorMessage)
return false, "Error while filtering text"
end
repeat task.wait() until filteredTextResult
return true, filteredTextResult:GetNonChatStringForUserAsync(player.UserId)
end
--
local Core = {}
function Core:FilterStringAsync(player, str)
return filterAsync(str, player)
end
return Core
Test Code Used
print(require(game:GetService("ServerStorage").Core):FilterStringAsync(game.Players:WaitForChild("fvazer"), "Hey, my phone number is 14581-457180-557271 "))