How do I prevent a command from sending with TextChatService?

My game is currently integrating to the new TextChatService. My game has custom chat commands which runs on the server (No, I am NOT using the built-in TextChatCommand provided by TextChatService) . When a chat command is ran, some commands will be sent out and be seen by everyone while some will not be seen by everyone.

This is the code when using Chat Service. A command function will be ran and it returns a boolean that indicates whether the chat message will be hidden or not.

game:GetService("Chat"):RegisterChatCallback(Enum.ChatCallbackType.OnServerReceivingMessage, function(msg)
	for _, plr in pairs (Players:GetPlayers()) do
		if plr.UserId == msg.SpeakerUserId then
			local hide = Commands.onCommand(plr, msg.Message)
			if hide then
				msg.ShouldDeliver = false
			end
			return msg
		end
	end
end)

After integrating with the new TextChatService, I tried to hide the chat message by calling ShouldDeliverCallback.

	local TextChatService = game:GetService("TextChatService")
	local generalChannel : TextChannel = TextChatService:WaitForChild("TextChannels"):WaitForChild("RBXGeneral")
	generalChannel.ShouldDeliverCallback = function(textChatMessage: TextChatMessage, targetTextSource: TextSource)
		if targetTextSource.UserId == textChatMessage.TextSource.UserId then
			local hide = Commands.onCommand(Players:GetPlayerByUserId(targetTextSource.UserId), textChatMessage.Text)
			return not hide
		end
	end

However, since ShouldDeliverCallback loops through every receiving player, some player will be able to see the hidden message and some players will not, depending on the looping order of ShouldDeliverCallback. Is there a workaround such that I can seamlessly achieve the old behavior?

It will also be ideal if the local player is not be able to see the hidden message, since apparently, ShouldDeliverCallback displays the message to local player even if it should not be delivered.

3 Likes

This doesn’t make any sense, isn’t ShouldDeliverCallback called for every client anyway?

It is called for every player. However, the order that it calls does not seems to be guaranteed. It is not like the player who initiated the command will trigger ‘ShouldDeliverCallback’ first. Which means some player will receive ‘ShouldDeliverCallback’ before the command function was even run.

I found a way you can stop chat messages from showing up whilst still allowing you to detect and run commands: By setting the TextChatMessage’s Text to an empty string within the TextChatService’s OnIncomingMessage callback, and doing so when the message’s status is Sending (I used a LocalScript inside of StarterPlayerScripts):

local TextChatService = game:GetService("TextChatService")

local HIDDEN_COMMAND = "/explode"

local function explode()
	print("Boom!")
end

local function onIncomingMessage(message: TextChatMessage)
	if message.TextSource and message.Status == Enum.TextChatMessageStatus.Sending then
		if message.Text == HIDDEN_COMMAND then
			explode()
			message.Text = "" -- Setting the message.text to an empty string will stop it from showing up in the chat
		end
	end
end

TextChatService.OnIncomingMessage = onIncomingMessage

Using the method above will prevent the message from being sent though, so it will need to be modified if you wish for certain players to be able to see that you ran the command in chat

The problem is the custom command system that I’ve written runs on the server only. The server gets to decide what command is going to be hidden.

Why does this order matter? Are you only running the command function through the callback? Why?

Most of TextChatService’s methods can only work on client-sided scripts, so dealing with chat messages on the server-side is, as you’re experiencing, difficult

You can execute the commands on the server-side using RemoteEvents, which would allow you to securely restrict which players can make them by checking if their user ID is in a whitelist


@Headstackk Examples on how to do the above:

Client-sided script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TextChatService = game:GetService("TextChatService")

local HIDDEN_COMMAND = "/explode"

local explodeEvent = ReplicatedStorage:WaitForChild("Explode")

local function onIncomingMessage(message: TextChatMessage)
	if message.TextSource and message.Status == Enum.TextChatMessageStatus.Sending then
		if message.Text == HIDDEN_COMMAND then
			explodeEvent:FireServer()
			message.Text = "" -- Setting the message.text to an empty string will stop it from showing up in the chat
		end
	end
end

TextChatService.OnIncomingMessage = onIncomingMessage
Server-sided script
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local explodeEvent = ReplicatedStorage.Explode

local whitelisted = {
	[5027420] = true
}

local function onServerEvent(player)
	if whitelisted[player.UserId] then
		print("Boom!")
	end
end

explodeEvent.OnServerEvent:Connect(onServerEvent)