Soft Mute / Shadow-ban Chat Feature?

Specifically without forking the chat service.

Has anyone ever used the default Roblox chat service to place a “shadow ban” on a ChatSpeaker?
To the chatter they would see their messages being sent, but no one else sees them.

I say this because I’ve had some spam issues in some of my games recently, and banning them just causes them to come back on alts.

2 Likes

Not sure how this would work out? Wouldn’t the player be able to realize that people or either ignoring them or something else is going on? They might join on an alt either way…

That is the effect of shadow-banning, yes, but does not answer my question.

1 Like

There are some ways to do the “Shadow-ban”, but probably you can’t do It without modifying the default chat module. I remember some short of modules and models that had the ability to soft mute, but I don’t remember the names.

If you could find 1 or more of the modules you mentioned, I would appreciate if you edited your post to include them.

In regards to having to fork the Roblox chat module, it is a shame since that’s probably the route I’m gonna end up taking. Basic toggle-able features seem to be lacking in the un-forked chat service. If I accomplish my goal from this thread then I’ll post an update with the feature in action.

I’ve worked quite a bit with the Lua Chat System. Although I haven’t ever made a shadow ban feature myself, I know it is possible to accomplish a feature like this.

Command functions will be your friend here. ChatModules will be run by the server to configure or extend the chat system. In the case of command functions, each of them are ran to process chats. We also have the ability to stop a chat from being processed through a command function by returning true.

Ever wonder why you can use /mute or /e without it showing up in the chat window? These are command functions. They run their respective functions, but also tell the ChatService not to continue processing the message. You can find more information on the documentation page I linked.

Since shadow banning is a server-side action, we also want to keep this system contained with the server. The idea is to swallow the chat but still send it to the client anyway. So with that in mind, we can get started on working to build this system.

You will need to create some of the directories that the Lua Chat System relies on, but you won’t need to fork anything. Start by creating the ChatModules folder in Chat. Add a BoolValue called InsertDefaultModules and make sure the value is checked off. This tells the Lua Chat System to fill the folder with the missing components at runtime. You’ll also want your module that tackles shadow bans, so you can go ahead and insert that as well.

When finished with the above setup, you should have a Chat service looking like this:

image

It’s all about editing the module from here. We’ll need at the very least the boilerplate for ChatModules (which should ideally be consistent). ChatModules typically return a function that will be called by the ChatService internally, passing itself to said function. Boilerplate code:

local function Run(ChatService)
	local function ProcessMessage(speakerName, message, channelName)
		
	end
	
	ChatService:RegisterProcessCommandsFunction("swallow_shadow_ban_chat", ProcessMessage)
end

return Run

Everything happens within the ProcessMessage function. Since I assume your shadow ban command will act directly on ChatSpeakers (which I can cover a bit later), we’re just going to perform a few simple checks on the ChatSpeaker regarding whether or not they have a shadow ban tag. If they do, then we’ll swallow the chat but continue to show it to them.

To skip some time on unnecessary explanations and all, I’ve supplied the complete code with comments throughout so you can understand what’s going into this ChatModule and what each step will contribute towards making this system work.

--- Swallow chats for shadow banned users.
-- @module SwallowShadowBanChats
-- @author colbert2677

local function Run(ChatService)
	local function ProcessMessage(speakerName, message, channelName)
		-- Get the speaker and the channel they sent a message in
		local speaker = ChatService:GetSpeaker(speakerName)
		local channel = ChatService:GetChannel(channelName)
		
		-- If neither exists, we're safe to continue processing via other methods
		if not speaker then return false end
		if not channel then return false end
		
		-- Check if they are shadow banned
		if speaker.ShadowBanned then
			-- Send the message to themselves
			speaker:SendMessage(message, channelName, speakerName, message.ExtraData)
			
			-- Speaker was shadow banned, stop processing message
			return true
		end
		
		-- Speaker is fine, continue processing message
		return false
	end
	
	ChatService:RegisterProcessCommandsFunction("swallow_shadow_ban_chat", ProcessMessage)
end

return Run

That’s pretty much all there is to it. Done. The rest of the effort will now come from you to determine how to insert and toggle the shadow ban tag in the ChatSpeaker object.

I have some test code that can also serve as a guiding line if you need that, though I trust that you already have your own way of doing this and you just needed to know how to stop messages from being sent to anyone except themselves.

Do not run this from the command bar. The command bar runs on a different VM, so it won’t be able to accurately execute this code because ChatSpeakers don’t exist for it. Create a new script in ServerScriptService with Disabled on and put this in. Execution steps will be included below.

local ServerScriptService = game.ServerScriptService
-- Need to access ChatService to work with its API
local ChatService = require(ServerScriptService:WaitForChild("ChatServiceRunner").ChatService)

-- Add the ShadowBanned tag to a ChatSpeaker for testing
local VictimSpeaker = ChatService:GetSpeaker("Player1")
VictimSpeaker.ShadowBanned = true

Player1 is now shadow banned as per the Chat system, but Player2 is not. To test if this holds true, start a local session of 2 players or more as you like. Go to the server view and enable the script by making Disabled false, then switch back to your clients. Send a message from Player1 and one from Player2. If this worked, Player1 should see both chats, but Player2 will only see their own.

And just like that. Shadow banned.

You could probably also write the shadow ban command via the ChatService if you follow the conventions of the mute speaker command. You’d just need to think in terms of using this shadow ban system rather than making it so that only the clients that muted a speaker don’t get the message.

Let me know if you have any questions or anything, always happy to help developers when it comes to working with the Lua Chat System.

I’ve supplied the system worked out above as a file in case you want to use a reference, want to use it directly how I set it up or want hands-on work with it:
Shadow Ban System.rbxl (137.0 KB)

12 Likes

This is brilliant, I could not have asked for a more thorough answer. You’ve made yourself a new fan today.

I will add to this thread once I’ve made this work with DataStores so that shadow banned users are permanently tagged.

Here is a demo:

My screen - https://gyazo.com/f2e276251705583da67ae9a4e53f7b11

Tyzone’s screen at the end of the gif - https://cdn.discordapp.com/attachments/689545866876551281/745726022472695858/ABC2.png

I’ll post a model for anyone to take soon.

My GUI was finished a few days after my last reply here, but because it was proprietary (made specifically for my group), I had no reason to share it on the DevForum. After I learned a minor lesson by putting my Discord webhook link directly into the publicly available script, I decided to re-do some backend work that would allow anyone to use this module.

Check out the main post here: ShadowMute - Mute people in chat without them knowing

1 Like