Chat Tag Script

When a player joins, this script checks through the tables to see if that players userID is listed in said tables. If it is, it gives them a chat tag.

Is there a way of shortening this?

local players = game:GetService("Players")
local marketplaceService = game:GetService("MarketplaceService")
local chatService = require(game.ServerScriptService:WaitForChild('ChatServiceRunner'):WaitForChild('ChatService'))

local developers = {ID, ID, ID}
local admins = {ID, ID}
local mods = {ID, ID, ID, ID}
local helpers = {ID, ID, ID, ID, ID, ID} 

chatService.SpeakerAdded:Connect(function(plr)
	local speaker = chatService:GetSpeaker(plr)
	local id = players:GetUserIdFromNameAsync(plr)
	-- Owner
	if players[plr].UserId == 61905980 then -- Me
		speaker:SetExtraData('Tags', {{TagText = 'OWNER', TagColor = Color3.fromRGB(36, 66, 39)}})
	end
	-- Developers
	for i, v in pairs(developers) do
		if id == v then
			speaker:SetExtraData('Tags', {{TagText = 'DEVELOPER', TagColor = Color3.fromRGB(230, 219, 64)}})
		end
	end
	-- Administrators
	for i, v in pairs(admins) do
		if id == v then
			speaker:SetExtraData('Tags', {{TagText = 'ADMIN', TagColor = Color3.fromRGB(88, 230, 230)}})
		end
	end
	-- Moderators
	for i, v in pairs(mods) do
		if id == v then
			speaker:SetExtraData('Tags', {{TagText = 'MOD', TagColor = Color3.fromRGB(42, 99, 230)}})
		end
	end
	-- Helpers
	for i, v in pairs(helpers) do
		if id == v then
			speaker:SetExtraData('Tags', {{TagText = 'HELPER', TagColor = Color3.fromRGB(5, 189, 5)}})
		end
	end
end)

(I had hidden my staff members’ userIDs for privacy reasons.)

5 Likes

If it’s for a group, you could check if they have a role in the group and if they do, give them the tag based on that. That’s what I do in my games.

Example from my game:

if not speaker:GetExtraData("Tags") then
			local Tags = {};
			local Player = speaker:GetPlayer();
			if Player then
				if Player:GetRankInGroup(game.CreatorId) >= 254 then --Developer
					--speaker:SetExtraData('NameColor', Color3.fromRGB(0, 162, 255));
					table.insert(Tags,{TagText = 'DEV', TagColor = Color3.fromRGB(0, 162, 255)});
				elseif Player:GetRankInGroup(game.CreatorId) == 251 then --Admin
					--speaker:SetExtraData('NameColor', Color3.fromRGB(199, 40, 102));
					table.insert(Tags,{TagText = 'ADMIN', TagColor = Color3.fromRGB(199, 40, 102)});
				elseif Player:GetRankInGroup(game.CreatorId) == 250 then --Moderator
					--speaker:SetExtraData('NameColor', Color3.fromRGB(204, 132, 55));
					table.insert(Tags,{TagText = 'YOUTUBER', TagColor = Color3.new(1, 0, 0)});
				end;
			end;
			speaker:SetExtraData("Tags", Tags);
		end;

Note: We forked the Roblox Chat so this is in the ExtraDataInitializer module, hence the “if not speaker:GetExtraData(“Tags”) then” line

3 Likes
local players = game:GetService("Players")
local marketplaceService = game:GetService("MarketplaceService")
local chatService = require(game.ServerScriptService:WaitForChild('ChatServiceRunner'):WaitForChild('ChatService'))

local playerIds = {}
local idColors = {}

playerIds["owners"] = {ID}
idColors["owners"] = Color3.fromRGB(36, 66, 39)

playerIds["developers"] = {ID, ID, ID}
idColors["developers"] = Color3.fromRGB(230, 219, 64)

playerIds["admins"] = {ID, ID}
idColors["admins"] = Color3.fromRGB(88, 230, 230)

playerIds["mods"] = {ID, ID, ID, ID}
idColors["mods"] = Color3.fromRGB(42, 99, 230)

playerIds["helpers"] = {ID, ID, ID, ID, ID, ID}
idColors["helpers"] = Color3.fromRGB(5, 189, 5)

chatService.SpeakerAdded:Connect(function(plr)
    local speaker = chatService:GetSpeaker(plr)
    local id = players:GetUserIdFromNameAsync(plr)

    for i, v in pairs(playerIds) do
        if table.find(v, id) then
            speaker:SetExtraData('Tags', {{TagText = string.upper(i:sub(1, -2)), TagColor = idColors[i]}})
            break
        end
    end
end)

tried remaking the script
not sure if this is a “shorter” way to do it

also if my code has bugs then I’m sorry

2 Likes

There are a couple of improvements you could make in terms of the structuring of your code as well as how you’re applying tags. I do encourage checking out the Lua Chat System API documentation for some tips and tricks you can use in your code.

In terms of how you’re assigning tags, you can use a dictionary to determine what kind of tag a player should have and keep a separate table of constants determining the data of a chat tag. This way you can essentially enumerate your chat tags and assign them nicely.

local CHAT_TAG_STYLES = {
    ["Owner"] = {TagText = "OWNER", TagColor = Color3.fromRGB(36, 66, 39)},
    -- etc
}

local SPECIAL_USERS = {
    [61905980] = CHAT_TAG_STYLES.Owner,
    -- etc
}

For SpeakerAdded, it’s imperative that you’re prepared for Deferred SignalBehavior becoming the default for when signals are fired as well as any potential race conditions arising out of your code wherein a speaker is added before the event connects. For this, take your lambda out of Connect and make a function variable for it. Connect it at the bottom then run it for every existing player.

local function speakerAdded(speakerName)
    -- Code goes here
end

chatService.SpeakerAdded:Connect(speakerAdded)
for _, speaker in ipairs(chatService:GetSpeakerList()) do
    task.spawn(speakerAdded, speaker)
end

A lot of developers working with the ChatService seem to not actually know that ChatSpeakers possess a GetPlayer method. Every player ChatSpeaker has their Player object attached which can be retrieved from the GetPlayer method. You should be using this over trying to find their player object in the Players service because it can also differentiate NPC speakers.

local speaker = chatService:GetSpeaker(speakerName)
local player = speaker:GetPlayer()

-- We do not want to add any tags to NPC speakers
if not player then return end

For the rest of your code, all you need to do is check if the player is in the special users dictionary using their UserId as a lookup key. If they exist, then you simply create a new table containing the tag that their UserId is assigned to. Since the special users table contains table references, you can directly pass in the value of the for loop as their chat tag. The dictionary’s great because you can avoid doing any unnecessary looping and instead directly check what tag a player should get.

Altogether:

local players = game:GetService("Players")
local marketplaceService = game:GetService("MarketplaceService")
-- EDIT: Please stay consistent. Changed this to use GetService.
local chatService = require(game:GetService("ServerScriptService"):WaitForChild('ChatServiceRunner'):WaitForChild('ChatService'))

local CHAT_TAG_STYLES = {
    ["Owner"] = {TagText = "OWNER", TagColor = Color3.fromRGB(36, 66, 39)},
    -- etc
}

local SPECIAL_USERS = {
    [61905980] = CHAT_TAG_STYLES.Owner,
    -- etc
}

local function speakerAdded(speakerName)
    local speaker = chatService:GetSpeaker(speakerName)
    local player = speaker:GetPlayer()

    -- We do not want to add any tags to NPC speakers
    if not player then return end

    -- Perform a lookup by their UserId; will return the table reference
    -- if it exists, or nil if it doesn't.
    local tagData = SPECIAL_USERS[player.UserId]
    if tagData then
        speaker:SetExtraData("Tags", {tagData})
    end
end

chatService.SpeakerAdded:Connect(speakerAdded)
for _, speaker in ipairs(chatService:GetSpeakerList()) do
    task.spawn(speakerAdded, speaker)
end

Fun fact: you can also add this as a ChatModule so that the ChatService initialises your module for you rather than having it as a separate script. It does require a couple of instances to be present so you can place your module there though. Ultimately there’s no difference between having it as a module ran by the ChatService or as a separate script beyond how ChatService gets defined in your code.

Just as an example, I substituted my UserId in the owner table to show you the result:

8 Likes

Thank you for taking your time to write all of that, it worked. :grin:

I have a question. If I were to implement like a gamepass check, how would I do it here?

1 Like

You could implement that manually the same way ExtraDataInitializer does. That is, you make a separate table with game pass ids and map them out to a CHAT_TAG_STYLE member. Depending on whether you want it to be additive or not, you can either add the game pass tag style to the current table of tags or add it only if they do not already have a tag.

If you have multiple game passes that award tags you’ll have to table up again but the difference is that you’ll need a for loop to run through each of the game passes and check if the player owns them before assigning them. If it’s just one, then no need for a table, you can do it directly in speakerAdded after the special tag check is performed.

In the case of adding game passes as well I’d probably divorce the table wrap of tagData and include it as an upvalue. That way, you can change the flow of your code to create the tags list → check which tags should be assigned to the player → set their data accordingly. Roughly:

local tags = {}

local specialTag = SPECIAL_USERS[player.UserId]
if specialTag then table.insert(tags, specialTag) end

local isVIP = gamePassCheckHere(GAME_PASS_ID) -- UserOwnsGamePassAsync
if isVIP then table.insert(tags, CHAT_TAG_STYLES.VIP) end

-- Fine even if they weren't given any tags. ExtraDataInitializer initialises
-- the tags table blank as well.
speaker:SetExtraData("Tags", tags)
1 Like