agentleFS
EL4CTEOEL4CTEO/roblox-skills/skills/roblox-text-chat/SKILL.md

roblox-text-chat

Skill92 starsChanged 47 days agoMITView on GitHub

Chat and text safety - TextChatService (channels, SendAsync, ShouldDeliverCallback, OnIncomingMessage, commands, bubbles, custom UI, platform-integrated chat and TextChannelWindow migration due May 2027), mandatory TextService filtering of any player-authored text, rate limits, privacy checks, PolicyService restrictions, legacy Chat migration. Use when adding chat features or commands, or any feature that shows one player's text to others (names, signs, notes).

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Not chat (no filtering needed): your own UI text, game status messages, admin announcements you write.

    line 20

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Chat and text safety

Non-negotiable rules (Roblox Community Standards)

  1. Player-to-player conversation must go through TextChatService TextChannels (handles filtering, privacy/parental settings, and abuse reporting). The legacy Chat service/ChatService Lua modules are gone — TextChatService is the only chat system.
  2. Any text authored by one player and shown to another (pet names, signs, guild names, trade notes, custom UI) must be filtered with TextService:FilterStringAsync on the server before display — and re-filtered when loaded from a DataStore later.
  3. Editable text others can see (signs, bulletin boards) needs a rate limit of at least 1 minute.
  4. Respect privacy settings with CanUserChatAsync/CanUsersChatAsync/CanUsersDirectChatAsync and mark direct-message channels with TextChannel:SetDirectChatRequester().

Not chat (no filtering needed): your own UI text, game status messages, admin announcements you write.

Filtering player-authored text

--!strict
local TextService = game:GetService("TextService")

-- Broadcast (e.g. a sign everyone sees). Returns nil if filtering failed: then show nothing.
local function filterForEveryone(text: string, fromUserId: number): string?
	local ok, result = pcall(function()
		local filterResult = TextService:FilterStringAsync(text, fromUserId, Enum.TextFilterContext.PublicChat)
		return filterResult:GetNonChatStringForBroadcastAsync()
	end)
	return if ok then result else nil
end

-- Per-recipient (respects each viewer's age/settings; e.g. a private note).
local function filterForUser(text: string, fromUserId: number, toUserId: number): string?
	local ok, result = pcall(function()
		local filterResult = TextService:FilterStringAsync(text, fromUserId, Enum.TextFilterContext.PrivateChat)
		return filterResult:GetNonChatStringForUserAsync(toUserId)
	end)
	return if ok then result else nil
end

return { forEveryone = filterForEveryone, forUser = filterForUser }
  • Filter on the server, after the player submits (not per keystroke); rate-limit and length-cap first.
  • On failure, fail closed (show nothing / a placeholder), never the raw text.
  • Store the raw text if you must, but always filter at display time (filters and viewer settings change).
  • Numbers-only or short strings can still be filtered (hashtags ###); design UI for that.

TextChatService essentials

  • Defaults: CreateDefaultTextChannels creates RBXGeneral and RBXSystem; CreateDefaultCommands adds built-ins (/whisper, /team, /mute, ...). Configure look via ChatWindowConfiguration, ChatInputBarConfiguration, BubbleChatConfiguration (or disable and build your own UI with TextChannel:SendAsync and TextChatService.MessageReceived).
  • Flow: client TextChannel:SendAsync(text) → server TextChannel.ShouldDeliverCallback(message, textSource) per recipient → filtering → clients' OnIncomingMessage callbacks → display.
  • Callbacks must not yield. ShouldDeliverCallback is server-only; TextChatService.OnIncomingMessage and TextChannel.OnIncomingMessage are client-only (formatting).
  • System messages: channel:DisplaySystemMessage(text) on the client (local only).
  • Custom channels (team, party, proximity): create TextChannels under TextChatService on the server, channel:AddUserAsync(userId) to add members; check the returned TextSource's CanSend.
--!strict
-- Client: chat tags and colors via OnIncomingMessage (formatting only; never yields).
local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")

TextChatService.OnIncomingMessage = function(message: TextChatMessage)
	local properties = Instance.new("TextChatMessageProperties")
	local source = message.TextSource
	if source then
		local player = Players:GetPlayerByUserId(source.UserId)
		if player and player:GetAttribute("VIP") then
			properties.PrefixText = `<font color="#F5CD30">[VIP]</font> {message.PrefixText}`
		end
	end
	return properties
end
--!strict
-- Server: a /spawn command, restricted to admins.
local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")

local ADMINS: { [number]: boolean } = { [123456] = true }

local command = Instance.new("TextChatCommand")
command.Name = "SpawnCommand"
command.PrimaryAlias = "/spawn"
command.Parent = TextChatService

command.Triggered:Connect(function(source: TextSource, unfilteredText: string)
	if not ADMINS[source.UserId] then
		return
	end
	local player = Players:GetPlayerByUserId(source.UserId)
	print(`{player and player.Name} ran: {unfilteredText}`)
end)

Platform-integrated chat (required May 2027)

Roblox is making chat native. Opt in to test now: select TextChatService in Studio and set PlatformIntegratedChat = Enabled (Studio only, not scriptable). When enabled:

  • Roblox renders all chat, in the default window or TextChannelWindows you place in UI (one per TextChannel via Target; works in SurfaceGuis). Custom chat UIs can no longer render messages.
  • Message text is hidden from code: TextChatMessage.Text/Translation/PrefixText, OnIncomingMessage, Player.Chatted, and TextChannel:SendAsync stop working (return "" + warning). No chat logging.
  • Commands must be TextChatCommands; preset phrases → Quick Words; chat tags/prefixes → role badges (planned Feb 2027 — don't opt in yet if you rely on prefixes). DisplaySystemMessage and BubbleChatConfiguration/OnBubbleAdded styling still work.
  • New code: build on TextChatCommand, TextChannelWindow, and system messages; avoid reading message text. The OnIncomingMessage tag example above only works until migration.

More: proximity chat, team channels, custom UI, and migrating from legacy chat: references/chat-recipes.md.

Quick Words (preset safe phrases for all ages, for coordinating where free chat is limited): add game-specific ones in Creator Hub → game → Configure → Chat → Quick Words — up to 3 categories of 3–10 phrases, in English (auto-translated), reviewed before going live. They replace the default categories; General stays.

PolicyService (per-user restrictions)

PolicyService:GetPolicyInfoForPlayerAsync(player) (server, pcall) returns flags such as ArePaidRandomItemsRestricted, IsPaidItemTradingAllowed, AreAdsAllowed, AllowedExternalLinkReferences, IsSubjectToChinaPolicies, IsContentSharingAllowed. Gate loot boxes, trading, ads, and links to social media (only show platforms listed in AllowedExternalLinkReferences) per player.

Related skills

roblox-security (rate limits, validation), roblox-ui (text input UI), roblox-monetization (paid random items), roblox-data-stores (storing user text).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.