The comments weren’t helpful and I did notice some really… bizzare design choices in your code that were just outright confusing. Here’s a rewrite of it with properly formatted comments:
-- SERVICES
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService('RunService')
local TextChatService = game:GetService("TextChatService")
-- OBJECT REFERENCES
-- > Folders
local TextChannels = TextChatService:FindFirstChild('TextChannels')
-- > Channels
local StaffChannel : TextChannel? = nil
local LogsChannel : TextChannel? = nil
-- > Events
-- NOTE: These should be loaded in before the script is started, there's very little reason to have a WaitForChild on these.
local LogsSystemMessage = ReplicatedStorage.Events.Other.LogEvent
local StaffSystemMessage = ReplicatedStorage.Events.Other.StaffEvent
-- FUNCTIONS
-- NOTE: We really don't need local functions in the topmost scope of the script.
-- [CreateChannel] - Creates a Text Channel of the given name and parents it to the instance provided.
--[[
Creates a Text Channel of the given name and parents it to the instance provided.<br>
<strong>name</strong> : The new name of the text channel.
<strong>parent</strong> : The parent to set this text channel to.
]]
function CreateChannel(name : string, parent : Instance?) : TextChannel
-- Create a new channel and rename it to the provided name.
local Channel = Instance.new("TextChannel")
Channel.Name = name
-- This is a fallback in the event we don't have a valid parent.
if (parent ~= nil) then
Channel.Parent = parent
else
Channel = TextChatService:FindFirstChild('TextChannels')
end
return Channel
end
-- NOTE: We could condense these into a single function and provide an optional event and message that developers can use.
-- However, that would make the script beginner-unfriendly if a developer isn't used to anonymous functions.
-- [AddToStaffChannel] - Adds the given player to the staff channel.
--[[
Adds the given player to the staff channel.<br>
<strong>player</strong> : The player to add to the channel.
]]
function AddToStaffChannel(player : Player)
if (StaffChannel ~= nil) then
StaffChannel:AddUserAsync(player.UserId)
StaffSystemMessage:FireClient(player, "Hello %s! Only staff can see and type in this channel.")
end
end
-- [AddToLogsChannel] - Adds the given player to the logs channel.
--[[
Adds the given player to the logs channel.<br>
<strong>player</strong> : The player to add to the channel.
]]
function AddToLogsChannel(player : Player)
if (LogsChannel ~= nil) then
local textSource = LogsChannel:AddUserAsync(player.UserId)
textSource.CanSend = false
LogsSystemMessage:FireClient(player, "Hello %s, this is the Logs Channel for system logs and messages only. These are for flagging events and other actions to assist in moderation.")
end
end
-- MAIN BODY
StaffChannel = CreateChannel("Staff", TextChannels)
LogsChannel = CreateChannel("Logs", TextChannels)
-- First setup the logs channel to deliver a callback.
-- > This is condensed to return true only if there is no text source or if there is no player for the text source.
LogsChannel.ShouldDeliverCallback = function(message : TextChatMessage, source : TextSource?) -- removes logs messages
return (message.TextSource == nil or Players:GetPlayerByUserId(message.TextSource.UserId) == false)
end
-- NOTE: Whenever a player is added, we'll check their group here, instead of in both functions.
-- That way we save a bit of extra time processing the request.
Players.PlayerAdded:Connect(function(player : Player)
if (player:GetRankInGroup(8423759) >= 6) then
AddToStaffChannel(player)
AddToLogsChannel(player)
end
end)
-- NOTE: Lastly, we only ever need to run this if we're in studio. Players shouldn't exist before the server in the default
-- server-client environment.
if (RunService:IsStudio()) then
for _, player in Players:GetPlayers() do
if (player:GetRankInGroup(8423759) >= 6) then
AddToStaffChannel(player)
AddToLogsChannel(player)
end
end
end
I would advise that you look at your entire code-base and refactor based on what each function and snippet is doing too.
If you’re seeing multiple fucntions that do the same thing with one or two values changing, like I saw createStaffChannel and createLogsChannel, then it’s a surefire sign to investigate further. You’ll see those functions condensed into CreateChannel(string, Instance).
I might just leave this code here and create a custom bubble chat system for myself. I don’t comment code because I work solo and don’t work with others in code, it’s also just a habit
If I can’t fork my code enough, I’m just going to mark your post as the solution and move on.
I used to do that. It’s a terrible habit that leads to the death of projects fast. If your first response to something you do is “it’s just a habit”, then do look into why it’s a habit and what effect that will have on your work in the long term.
A bubble chat system doesn’t need to be custom. Trying to make a fully-custom one is a terrible idea when you can simply disable the automatic text bubbles and use TextChatService:DisplayBubble for manual text display when a text channel receives a message for the local client to display.
local TextChatService = game:GetService("TextChatService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local logWelcomeEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("Other"):WaitForChild("LogEvent")
local staffWelcomeEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("Other"):WaitForChild("StaffEvent")
local logMessageEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("Other"):WaitForChild("SendLog")
local function sendWelcomeMessage(channelName, message)
local channel = TextChatService:WaitForChild("TextChannels"):WaitForChild(channelName)
if channel then
channel:DisplaySystemMessage(string.format("<font color='#D3D3D3'>%s</font>", message:format(Players.LocalPlayer.Name)))
end
end
staffWelcomeEvent.OnClientEvent:Connect(function(message)
sendWelcomeMessage("Staff", message)
end)
logWelcomeEvent.OnClientEvent:Connect(function(message)
sendWelcomeMessage("Logs", message)
end)
logMessageEvent.OnClientEvent:Connect(function(formattedText)
local channel = TextChatService:WaitForChild("TextChannels"):WaitForChild("Logs")
if channel then
channel:DisplaySystemMessage(formattedText)
end
end)
TextChatService.OnIncomingMessage = function(message: TextChatMessage)
if message.TextSource then
local player = Players:GetPlayerByUserId(message.TextSource.UserId)
if player and message.TextChannel and message.TextChannel.Name ~= "Logs" then
TextChatService:DisplayBubble(player.Character, message.Text)
end
end
end
local chatWindowConfiguration = TextChatService.ChatWindowConfiguration
TextChatService.OnChatWindowAdded = function(message: TextChatMessage)
if message.TextSource then
if message.TextChannel.Name ~= 'Logs' then
return
end
if message.TextSource.UserId == Players.LocalPlayer.UserId then
local properties = chatWindowConfiguration:DeriveNewMessageProperties()
properties.PrefixText = " "
properties.Text = " "
return properties
end
end
end
This works, although it breaks literally every script related to the text channels.
How can I integrate that? Edit the code I send below please (doesn’t have to be a working solution, just something I can use as a base)
local textChatService = game:GetService("TextChatService")
local players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local chatWindowConfiguration = textChatService.ChatWindowConfiguration
local bindable = ReplicatedStorage:WaitForChild("Events"):WaitForChild("Other"):WaitForChild("DisguiseMod")
local function getPlayerName(player: Player)
return player.Name
end
local Tags = {
[255] = {"<font color='#d54e50'><b>[Owner]</b></font>", "<font color='#d54e50'>%s:</font>"},
[9] = {"<font color='#d54e50'><b>[Manager]</b></font>", "<font color='#d54e50'>%s:</font>"},
[8] = {"<font color='#bc49ff'><b>[Developer]</b></font>", "<font color='#bc49ff'>%s:</font>"},
[7] = {"<font color='#00c3ff'><b>[Admin]</b></font>", "<font color='#00c3ff'>%s:</font>"},
[6] = {"<font color='#ff8902'><b>[Moderator]</b></font>", "<font color='#ff8902'>%s:</font>"},
[5] = {"<font color='#ff77ed'><b>[Translator]</b></font>", "<font color='#ff77ed'>%s:</font>"},
[4] = {"<font color='#ff77ed'><b>[Contributor]</b></font>", "<font color='#ff77ed'>%s:</font>"},
[3] = {"<font color='#a7ff34'><b>[Friend]</b></font>", "<font color='#a7ff34'>%s:</font>"},
[2] = {"<font color='#a7ff34'><b>[Partner]</b></font>", "<font color='#a7ff34'>%s:</font>"},
[1] = {"<font color='#a7ff34'><b>[Gang]</b></font>", "<font color='#a7ff34'>%s:</font>"}
}
textChatService.OnIncomingMessage = function(message: TextChatMessage)
local properties = Instance.new("TextChatMessageProperties")
if message.TextSource then
local player = players:GetPlayerByUserId(message.TextSource.UserId)
if player then
local playerName = getPlayerName(player)
local rankInGroup = player:GetRankInGroup(8423759)
if Tags[rankInGroup] then
local prefix = Tags[rankInGroup][1]
local formattedMessage = string.format(Tags[rankInGroup][2], playerName)
properties.PrefixText = prefix .. " " .. formattedMessage
end
end
end
return properties
end
bindable.OnClientEvent:Connect(function(executor, toggle, rank)
local playerName = getPlayerName(executor)
print("Executor Name:", playerName)
print("Toggle:", toggle)
print("Rank:", rank)
local properties = Instance.new("TextChatMessageProperties")
if toggle then
if rank == "Guest" then
properties.PrefixText = string.format("%s:", playerName)
print("Rank set to Guest.")
elseif rank == "Gang" then
if Tags[1] then
local prefix = Tags[1][1]
local formattedMessage = string.format(Tags[1][2], playerName)
properties.PrefixText = prefix .. " " .. formattedMessage
print("Rank set to Gang.")
end
elseif rank == "Partner" then
if Tags[2] then
local prefix = Tags[2][1]
local formattedMessage = string.format(Tags[2][2], playerName)
properties.PrefixText = prefix .. " " .. formattedMessage
print("Rank set to Partner.")
end
elseif rank == "Friend" then
if Tags[3] then
local prefix = Tags[3][1]
local formattedMessage = string.format(Tags[3][2], playerName)
properties.PrefixText = prefix .. " " .. formattedMessage
print("Rank set to Friend.")
end
end
else
if rank == nil then
local rankInGroup = executor:GetRankInGroup(8423759)
print("Rank in Group:", rankInGroup)
if Tags[rankInGroup] then
local prefix = Tags[rankInGroup][1]
local formattedMessage = string.format(Tags[rankInGroup][2], playerName)
properties.PrefixText = prefix .. " " .. formattedMessage
print("Reverted to original rank.")
end
end
end
print("Properties:", properties.PrefixText)
end)
While I can’t give a direct fix to you for the more complex issue, as it was still untested at time of writing, I can at least confirm my suggested implementation works. All you need to do is use it in a TextChannel.OnIncomingMessage callback.
While working on the latest updates for my own chat implementation, the prototype/proof-of-concept for chat bubble visibility has been added as an optional feature. It’s the most simple implementation of restricted-visibility bubble chats. You do need to have BubbleChatConfiguration.Enabled set to false for this to work.
In the image above, OnIncomingMessage is part of a ModuleScript. The function alongside all other modules with that function are called and given the channel the message came from and incoming message. You may need to check if the player and sender are in the same channel depending on how you’re handling TextChannel messages.
However, you will loose all ability to customize the appearance of chat bubbles if you want to have custom privacy for chat bubbles with the default bubble chat UI. If your experience has custom chat bubble appearances for ranks or paid passes/subscriptions, this will break that.
I have tried to make customization work, but it breaks because BubbleChatConfiguration.Enabled is false. That needs to be true for customization, which in turn breaks the privacy of messages to just the channels they’re sent in.
Please don’t do unnecessary replies like these. All you did with this was advertise WinryChat, a pretty bad custom chat.
Anyway, there’s no way to disable these client-sided bubbles without creating a custom chat system. Maybe just let these slide? They’re only visible for the client.
The point of the reply was to show how I implemented it in a prototype and the restrictions you face as a result. I could remove the reference to the custom chat system entirely because it’s just not the point of the post (hence why it wasn’t hyperlinked).
While I do appreciate feedback too, please at least take the time to explain what is wrong so it can be addressed. Blindly saying “a pretty bad [blank]” is a nothing-statement. Doesn’t tell us what to fix, doesn’t tell us what to remove, doesn’t tell us what to add.
I’ll edit out the direct references and amend the post, but please send the direct and exact issues you have in PMs. That way they can be addressed without derailing this topic.