OnPlayerChat
OnPlayerChat is called when a connected player sends a chat message that is not a chat command, after the game has escaped rich text and before the message is broadcast to anyone.
object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The connected BasePlayer who sent the message. Never null at this point: offline or disconnected senders are routed to OnPlayerOfflineChat instead. |
message | string | The chat text after rich text markup has been stripped with EscapeRichText. Chat command messages (starting with the command prefix) never reach this hook. |
channel | ConVar.Chat.ChatChannel | The Chat.ChatChannel the message was sent on: Global, Team, Server, Cards or Local. |
Return value
Returning null keeps the message flowing through the normal chat path and the result of OnUserChat is returned instead. Returning any non-null object (commonly true) makes IOnPlayerChat return that object immediately, which suppresses the default handling of that message.
How it works
Oxide fires the hook from RustCore.IOnPlayerChat, which is the entry point for every player chat line. Before the hook runs, the server drops empty messages and the literal text text, forwards anything with the chat command prefix to TryRunPlayerCommand and returns, and applies EscapeRichText to the message. Offline or disconnected senders go to OnPlayerOfflineChat instead, so player here is always a live, connected BasePlayer.
Return a non-null value to suppress the default chat handling for that message: the original method returns your value and the message is not shown to other players (and no OnUserChat result is used). Return null to let the message through normally. channel lets you treat Global, Team and Local lines differently. Because the hook fires for every chat line, keep the body cheap and avoid logging on the hot path. Note that rich text tags are already escaped when you receive message, so you cannot re-inject color codes by returning the same string.
Example plugin: Silence and flag chat messages containing blocked words
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Chat Word Filter", "RustPluginGenerator", "1.0.0")]
[Description("Blocks chat messages containing configured words and alerts staff")]
public class ChatWordFilter : RustPlugin
{
private const string PermNotify = "chatwordfilter.notify";
private PluginConfig config;
private Regex blockedPattern;
private class PluginConfig
{
[JsonProperty("Blocked words")]
public List<string> BlockedWords = new List<string> { "freeitems", "cheats" };
[JsonProperty("Filter team chat")]
public bool FilterTeamChat = true;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Blocked"] = "Your message was hidden: it contains a blocked word.",
["StaffAlert"] = "{0} tried to send a filtered message: {1}"
}, this);
}
private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermNotify, this);
BuildPattern();
}
private void BuildPattern()
{
var words = new List<string>();
foreach (var word in config.BlockedWords)
{
if (!string.IsNullOrEmpty(word)) words.Add(Regex.Escape(word.ToLowerInvariant()));
}
blockedPattern = words.Count > 0 ? new Regex(@"\b(" + string.Join("|", words) + @")\b", RegexOptions.IgnoreCase) : null;
}
private object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)
{
if (player == null || string.IsNullOrEmpty(message) || blockedPattern == null) return null;
if (!config.FilterTeamChat && channel == ConVar.Chat.ChatChannel.Team) return null;
if (!blockedPattern.IsMatch(message)) return null;
player.ChatMessage(Msg("Blocked", player.UserIDString));
foreach (var target in BasePlayer.activePlayerList)
{
if (target == null || !target.IsConnected) continue;
if (target.UserIDString == player.UserIDString) continue;
if (permission.UserHasPermission(target.UserIDString, PermNotify))
{
target.ChatMessage(Msg("StaffAlert", target.UserIDString, player.displayName, message));
}
}
return true;
}
}
}The plugin loads a list of blocked words from its config and lowercases them once on load. In OnPlayerChat it scans the incoming message for a whole-word match using a regex built from the escaped word list. When a match is found it sends a warning only to the sender through the language file, alerts staff with the chatwordfilter.notify permission, and returns true to stop the message from reaching anyone else. Unfiltered messages return null so normal chat continues. Matches against teammates on the Team channel are filtered the same way by default, and a config flag lets admins disable filtering for the Team channel if they use it for coordination.
Common use cases
Block banned words or links
Return true when a message matches a blocked pattern so spam, advertisements or slurs never reach the chat buffer. Keep the word list in config so admins can edit it without recompiling.
Per-channel gating
Use the channel argument to redirect or reject messages: for example deny Global chat for players without a permission while leaving Team chat untouched.
Audit trail for staff
Log matched messages with the sender's name and Steam ID to a data file and notify online admins via permission, without altering normal chat.
Pitfalls
Commands never reach the hook
Anything starting with the chat command prefix is consumed by the command handler before OnPlayerChat is called, so you cannot filter or intercept plugin commands here.
Rich text is already escaped
The message passed in has been through EscapeRichText, so returning it unchanged cannot restore color codes and any tags the player typed are shown literally.
Offline senders are excluded
If the player is disconnected the server calls OnPlayerOfflineChat instead. Handle that hook too if you need to cover messages from the Rust+ app or reconnect edge cases.
Plugin recipes that use OnPlayerChat
- How to make a Rust chat anti-spam plugin (Oxide/Carbon)Blocks chat messages that exceed a rate limit or duplicate the player's previous message, and mutes repeat offenders for a configurable time. It uses the OnPlayerChat hook and returns a non-null object to cancel a message.
- How to make a Rust chat logger plugin (rolling log + search)Records every chat message with timestamp, SteamID, name, channel and text into a capped oxide/data file, and adds a /chatlog command that prints a player's recent messages. Uses the OnPlayerChat hook with BasePlayer, string and Chat.ChatChannel.
- How to make a Rust chat mentions plugin (@name pings)Detects @name tokens in chat and sends the mentioned online player a private notice plus a notification sound, with a per-sender cooldown. Uses OnPlayerChat.
- How to make a Rust chat mute plugin (timed, persistent)A timed chat mute plugin: `/mute <player> <minutes> [reason]` stores a mute with an expiry in a data file, `/unmute <player>` clears it, and `OnPlayerChat` blocks global chat from muted players while team and local chat keep working.
- How to make a Rust chat titles and colors plugin (Oxide group tags)Replaces vanilla global chat with formatted lines: a group tag plus separate name and message colors picked by the player's highest-priority Oxide group, keeping the speaker's avatar. Uses OnPlayerChat to cancel the original message and send the formatted one.
- How to make a Rust chat word filter plugin (whole-word asterisk masking)This plugin replaces blocked words in player chat with asterisks using case-insensitive whole-word matching and re-broadcasts the cleaned line under the sender's name. It hooks OnPlayerChat and returns a non-null value to suppress the original message.
- How to make a Rust clan plugin (tag, invite, friendly fire block)Adds a lightweight clan system with /clan create, invite, join, leave and info, stores clans in a data file, prefixes members' chat with their tag and blocks damage between clanmates via OnPlayerChat and OnEntityTakeDamage.
- How to make a Rust team tags in chat plugin (Oxide/Carbon)Lets the leader of a Rust team pick a 2-5 character tag with /tag, stores it per team in oxide/data, and prefixes every team member's chat line with [TAG] inside an OnPlayerChat hook that formats the message.
Build a plugin with OnPlayerChat
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPlayerChat
Generate