Roblox Chat Api Question

So I am making something for an admin panel and I ran into an issue where I want to make it so administrators can “disguise” themselves on the Roblox chat as another player, but Roblox provides not documentation on how to change the speaker tag of another player. I’ve seen this used in several other games so I will assume this is not against ToS. Does anyone know how to approach such a thing?

Edit: Simply I just want to know how to change the speaker tag of a player using Roblox’s chat API as I can’t find documentation on such a thing but I know it is possible as I’ve seen other games do it. By speaker tag I mean the [Speaker]: part of the message in the Roblox chat.

3 Likes

If I understood this post correctly, I don’t think impersonating another player will be taken in a positive sense and you should avoid doing this.

2 Likes

This is already done in other games. I just want to know how it’s done as Roblox provides support to do such a thing. Why would Roblox provide support to do something that is malicious? It obviously wouldn’t be used with malicious intent in this scenario but it could be.

Edit: I understand how it wouldn’t be taken in a positive sense but Roblox provides support for it with the chat API so it’s obviously allowed as Roblox wouldn’t provide support for something that shouldn’t happen, I just want to know how I would use Roblox’s chat API to make something like this happen.

1 Like

Many games use this, Electric State has a direct ability to change to another player and as talk to them in chat.

In my opinion this is not quite appropriate and I am surprised that this is allowed. You could change your name to another player who is possibly not 13+ and then attempt to bypass the filter. This message could be displayed to other uses and the player you are impersonating could then be falsely reported or moderated.

I get what you mean, but it doesn’t answer my question. I just want to know how it’s done. If you want to further this conversation you can PM me on Devforum. I would assume Roblox has something to prevent what you’ve mentioned though.

1 Like

Yes there is a way. You can do that using the Lua Chat System api.

Basically, you need to use the RegisterProcessCommandsFunction to check if someone whose name you would like to spoof just chatted. If so then create a fake speaker and make it say the exact same thing. Then return true so the original message gets hidden.

Example (untested but should work):

local Names = {
    CleverSource = "Player1"
}

local ChatService = require(game:GetService("ServerScriptService"):WaitForChild("ChatServiceRunner"):WaitForChild("ChatService"))

local All = ChatService:GetChannel("All") --grab the default channel
All:RegisterProcessCommandsFunction("disguising", function(name, msg, channel) --register the function
    local n = Names[name]
    if n then
        local FakeSpeaker = ChatService:GetSpeaker(n)
        if not FakeSpeaker then --create the fake speaker if it isn't created
            FakeSpeaker = ChatService:CreateSpeaker(n)
            FakeSpeaker:JoinChannel("All")
        end
        FakeSpeaker:SayMessage(msg, channel, {}) --say the messaage
        return true --hide the original message
    end
    return false
end)

Further reading:
Lua Chat System

Chat Service
Chat Speaker
Chat Channel
Chat Message

Edit: Don’t forget to filter the message!

5 Likes

Along with @Amiaa16’s method, another way to achieve this effect (that doesn’t require creating or getting any new speakers or touching channels) is through ChatService:RegisterFilterMessageFunction.

We’ll also directly add a ModuleScript to our own ChatModules folder inside the Chat service, which avoids having to yield for a second or two while waiting for “ChatServiceRunner” to be parented to ServerScriptService:

image

You can get these objects by copying and pasting them from a test play and removing the extra instances, then adding a ModuleScript named “Disguise” (or any other fitting name). Make sure that InsertDefaultModules is enabled. ModuleScripts parented to ChatModules are expected to return functions, which are invoked once when the chat system is initialised. The function is passed the ChatService object, and is simply given the chance to register functions during initialisation. They serve no other purpose.

The function we need is used as void ChatService:RegisterFilterMessageFunction(string functionId, func func). It registers a function to the chat identified by functionId used as a filter that is invoked with every sent message. The function is passed the speaker’s name, the ChatMessage object, and the ChatChannel the message originated in. Any changes to the message will persist and be displayed when the message makes it through all of the other filter functions:

-- void run(table chatService)
local function run(chatService)
	chatService:RegisterFilterMessageFunction("Disguise", function(speakerName, message)
		-- ...
	end)
end

return run

We’ll create a dictionary with keys as the original speaker names and values as the disguise names for simplicity’s sake. The admin system should edit this dictionary (or use something else entirely) accordingly:

-- Dictionary<string,string>
local nameDisguises = {
	["Dandystan"] = "Kiriot22",
	["LordHammy"] = "CleverSource",
	["ThatUnfitBuck"] = "Dandystan",
}

If a speaker called Dandystan sends a message, their name will be changed to Kiriot22. We’ll index this dictionary within the filter function to check if the speaker is disguised. We’ll just return from the function if there’s no disguise:

-- find disguise
local disguise = nameDisguises[speakerName]
if not disguise then return end

Then, all we have to do is change the ChatMessage.FromSpeaker to the disguise name, and change the message’s name colour to the colour associated with the new name (yes, each player name has a colour that never changes). To change the speaker name:

-- change name
message.FromSpeaker = disguise

To change the colour appropriately, we’ll directly edit ChatMessage.ExtraData to change its "NameColor" element using an algorithm found in the chat system (that’s cleaned up a bit :wink: ):

-- Array<Color3>
local nameColors = {
	Color3.fromRGB(253, 41, 67), -- BrickColor.new("Bright red").Color,
	Color3.fromRGB(1, 162, 255), -- BrickColor.new("Bright blue").Color,
	Color3.fromRGB(2, 184, 87), -- BrickColor.new("Earth green").Color,
	BrickColor.new("Bright violet").Color,
	BrickColor.new("Bright orange").Color,
	BrickColor.new("Bright yellow").Color,
	BrickColor.new("Light reddish violet").Color,
	BrickColor.new("Brick yellow").Color,
}

-- int getNameValue(string name)
local function getNameValue(name)
	local value = 0
	for index = 1, #name do
		local cValue = name:sub(index, index):byte()
		local reverseIndex = #name - index + 1
		if #name % 2 == 1 then
			reverseIndex = reverseIndex - 1
		end
		if reverseIndex % 4 >= 2 then
			cValue = -cValue
		end
		value = value + cValue
	end
	
	return value
end

-- ...

message.ExtraData.NameColor = nameColors[getNameValue(disguise) % #nameColors + 1]

Putting everything together, the code should look like this:

-- variables:
-- Dictionary<string,string>
local nameDisguises = {
	["Dandystan"] = "Kiriot22",
	["LordHammy"] = "CleverSource",
	["ThatUnfitBuck"] = "Dandystan",
}
-- Array<Color3>
local nameColors = {
	Color3.fromRGB(253, 41, 67), -- BrickColor.new("Bright red").Color,
	Color3.fromRGB(1, 162, 255), -- BrickColor.new("Bright blue").Color,
	Color3.fromRGB(2, 184, 87), -- BrickColor.new("Earth green").Color,
	BrickColor.new("Bright violet").Color,
	BrickColor.new("Bright orange").Color,
	BrickColor.new("Bright yellow").Color,
	BrickColor.new("Light reddish violet").Color,
	BrickColor.new("Brick yellow").Color,
}

-- functions:
-- int getNameValue(string name)
local function getNameValue(name)
	local value = 0
	for index = 1, #name do
		local cValue = name:sub(index, index):byte()
		local reverseIndex = #name - index + 1
		if #name % 2 == 1 then
			reverseIndex = reverseIndex - 1
		end
		if reverseIndex % 4 >= 2 then
			cValue = -cValue
		end
		value = value + cValue
	end
	
	return value
end
-- void run(table chatService)
local function run(chatService)
	chatService:RegisterFilterMessageFunction("Disguise", function(speakerName, message)
		-- find disguise
		local disguise = nameDisguises[speakerName]
		if not disguise then return end
		-- change name
		message.FromSpeaker = disguise
		-- handle name colour
		message.ExtraData.NameColor = nameColors[getNameValue(disguise) % #nameColors + 1]
	end)
end

return run

https://gyazo.com/a0e564a599e66d694fbe0b74eb818610

9 Likes

Woah, how long did it take you to type that :open_mouth:
respect

But yea your method is good too. The reason I don’t want to touch RegisterFilterMessageFunction is because it has some bugs when doing stuff like what you do.
If I recall correctly, changing someone’s message’s text size to 0 or their name with that function caused new players who joined to have their chat broken and not working. Not sure if it has or hasn’t been fixed since then /shrug

Other than that, it’s all good :+1:
(and I don’t smell!!)

3 Likes

Thank you both for the great replies! If I could give you both the solution I would, but you both helped greatly. :+1:

While your method works, only the client can see the messages. Everyone on the server sees a filtered messages no matter what message content is sent.