# esx_rpchat (/docs/addons/esx_rpchat)

esx_rpchat adds the classic roleplay chat commands to an ESX Legacy server: `/me` and `/do` for nearby players, a global OOC channel, `/twt` and `/anontwt` "tweets", and `/msg` private messages. Every command is rate limited on the server, and proximity messages are filtered by distance and routing bucket.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_rpchat` |
| Side | Server commands, small client handler |
| Dependencies | `esx_lib`, `es_extended`, the default `chat` resource |
| Database | None |

## Installation [#installation]

```bash title="server.cfg"
ensure chat
ensure esx_lib
ensure es_extended
ensure esx_rpchat
```

## Commands [#commands]

| Command | Who sees it | Prefix (en locale) | Color |
| --- | --- | --- | --- |
| (plain message) | Everyone | `OOC \| Name` | grey |
| `/me <text>` | Players within `ProximityDistance` | `me \| Name` | red |
| `/do <text>` | Players within `ProximityDistance` | `do \| Name` | blue |
| `/twt <text>` | Everyone | `[Twitter] (@Name)` | light blue |
| `/anontwt <text>` | Everyone | `[Twitter] (@Anonymous)` | light blue |
| `/msg <id> <text>` | Target and sender | `PM from Name[id]` / `PM SEND TO Name[id]` | orange |

- `/me` describes an action ("/me opens the trunk"); `/do` describes the scene or answers a `/me` ("/do The trunk is empty").
- `/me` and `/do` text is cut to `ProximityMaxLength` characters (UTF-8 aware). `/msg` uses the same limit.
- `/msg` ignores empty messages, messages to yourself and invalid ids. It uses FiveM names, not character names.
- None of the commands can be used from the server console.

<Callout type="info">
OOC works through the legacy `chatMessage` event: esx_rpchat cancels every non-command message and re-broadcasts it with its prefix. If another chat resource also handles `chatMessage`, messages can appear twice.
</Callout>

## Configuration [#configuration]

| Key | Default | Description |
| --- | --- | --- |
| `Config.Locale` | `GetConvar('esx:locale', 'en')` | Language for prefixes and help text |
| `Config.OnlyFirstname` | `false` | Show only `firstName` instead of the full name |
| `Config.EnableESXIdentity` | `true` | Use the character name (`xPlayer.getName()`); `false` uses the FiveM name |
| `Config.ProximityDistance` | `20.0` | Radius in meters for `/me` and `/do` |
| `Config.ProximityMaxLength` | `256` | Max characters kept for `/me`, `/do` and `/msg` |
| `Config.OocCooldown` | `3000` | Milliseconds between OOC messages per player |
| `Config.TwtCooldown` | `10000` | Milliseconds between `/twt` |
| `Config.AnonTwtCooldown` | `15000` | Milliseconds between `/anontwt` |
| `Config.MeCooldown` | `1000` | Milliseconds between `/me` |
| `Config.DoCooldown` | `1000` | Milliseconds between `/do` |

The server also reads `Config.MsgCooldown` for `/msg`. It is not in the shipped `config.lua`, so it falls back to `1000`. Add it if you want a different value. Setting any cooldown to `0` disables that limit.

A rate-limited player gets a red `SYSTEM` message like "Please wait 3 seconds before sending another message."

## Events [#events]

### esx_rpchat:sendProximityMessage (client) [#esx_rpchatsendproximitymessage-client]

The server sends `/me` and `/do` to nearby players through this event:

```lua
-- server
xLib.triggerClientEvent('esx_rpchat:sendProximityMessage', targets, title, message, color)
```

The target list comes from `ESX.OneSync.GetPlayersInArea` in the sender's routing bucket, so players in other instances (apartments, garages) do not see the message.

The client handler also accepts an older 4-argument form `(playerId, title, message, color)` and then checks the distance to that player locally. You can reuse the 3-argument form from your own server scripts to send any styled chat line to a list of players:

```lua title="server/main.lua"
local targets = { source, otherId }
xLib.triggerClientEvent('esx_rpchat:sendProximityMessage', targets, 'radio | Dispatch', '10-4, units en route', { 0, 200, 0 })
```

## Integration tips [#integration-tips]

- **Logging:** `/anontwt` is anonymous to players but not to the server. If you need moderation, add a log call in the `anontwt` command in `server/main.lua`.
- **Localization:** prefixes contain chat color codes (`^0`, `^4`, `^5`). Edit `locales/<lang>.lua` to restyle them.
- **Custom chat UIs:** messages are sent through `chat:addMessage`, so any chat resource that implements it works.

## Common issues [#common-issues]

- **OOC messages show twice:** another resource also re-broadcasts `chatMessage`. Disable one of them.
- **/me not visible to a nearby player:** the player is in a different routing bucket, or OneSync is off (`ESX.OneSync.GetPlayersInArea` needs OneSync).
- **Names show as FiveM names:** the player has no character name yet, or `EnableESXIdentity` is `false`.

## Related [#related]

- [esx_identity](/docs/core/esx_identity)
- [esx_chat_theme](/docs/core/esx_chat_theme)
- [OneSync functions](/docs/es_extended/server/onesync)
- [Commands](/docs/es_extended/commands)
