ngd-chat

A feature-rich custom chat system for FiveM with multi-channel support, 3D bubbles, staff tools, Discord/FiveManage logging, and full player customization.

ngd-ui

Installation

  1. Place ngd-chat into your [ngd] folder. This script must be started after ngd-Bridge.
  2. Remove the default FiveM chat resource
  3. Configure your server.cfg convars (see below)
WARNING

ngd-chat replaces the default FiveM chat. Do not run both at the same time.


Server.cfg Setup

Add these convars to your server.cfg as needed:

# Discord webhook for chat logging (required if Config.UseDiscordLog = true)
set ngd_chat_webhook "https://discord.com/api/webhooks/YOUR_WEBHOOK_HERE"

# FiveManage API key for log ingest (required if Config.UseFivemanageLog = true)
set ngd_chat_fivemanage_key "YOUR_API_KEY"

Chat Channels

ngd-chat ships with the following channels out of the box. All channels are configurable in config.lua.

ChannelCommand(s)ScopeDescription
Global/g, /globalServer-wideDefault chat channel
Proximity/p, /proximity15m radiusNearby players only
Advertisement/ad, /advertServer-wideJob-locked with 60s cooldown
Police/policeJob-onlyPolice announcements with animated border
EMS/ems, /eJob-onlyEMS announcements
Me/me20m radiusAction bubble above head
Do/do20m radiusNarration bubble above head
Anonymous/anonServer-wideHidden sender identity, blocked from configured jobs
Staff/staffStaff-onlyRequires staff permission
Announce/announceServer-wideToast notification with sound

Adding a Custom Channel

Add a new entry to Config.Channels in config.lua:

mychannel = {
    command = {'mychannel', 'mc'},
    color = '#ff6b6b',
    label = 'MY CHANNEL',
    description = 'A custom channel',
    scope = 'all',          -- 'all', 'proximity', 'job', 'staff'
    -- distance = 15.0,     -- required for 'proximity' scope
    -- jobs = {'police'},   -- required for 'job' scope
    -- permission = 'command', -- required for 'staff' scope
    -- cooldown = 30,       -- optional cooldown in seconds
    -- bubble = true,       -- show as 3D bubble above head
    -- bubbleOnly = true,   -- only show as bubble, not in chat
    -- icon = 'myicon.png', -- image from /icons folder
    -- animated = true,     -- snake border animation
}

Configuration

General Settings

Config.DefaultChannel = 'global'     -- channel used when typing without a /prefix
Config.MaxMessageLength = 256        -- max characters per message
Config.BubbleDuration = 8            -- seconds /me and /do bubbles stay visible
Config.StaffPermission = 'command'   -- framework permission group for staff tools
Config.ShowPlayerIdForStaff = true   -- show [ID] next to names for staff members

Rate Limiting

Config.RateLimit = {
    messages = 3,    -- max messages allowed
    seconds = 3,     -- within this time window
}

Word Filter

Config.WordFilter = {
    enabled = true,
    action = 'censor',    -- 'censor' replaces with ***, 'block' prevents the message
    words = {
        'badword',
        'slur',
    },
}
INFO

The word filter is case-insensitive.

Logging

-- Discord webhook logging
Config.UseDiscordLog = true

-- FiveManage log ingest
Config.UseFivemanageLog = false

Both can be enabled simultaneously.

Timed Messages

Automatically broadcast rotating messages at a set interval:

Config.TimedMessages = {
    enabled = true,
    interval = 15,              -- minutes between each message
    name = 'Server',            -- display name
    color = '#3fb0e5',          -- message color (CSS hex)
    messages = {
        'Join our Discord at discord.gg/example!',
        'Report bugs using /report in-game.',
        'Check out our store at store.example.com!',
    },
}

Messages cycle through the list in order, one per interval.

Announcements

Config.AnnouncePosition = 'top-center'  -- toast position on screen
Config.AnnounceDuration = 8             -- seconds the toast stays visible
Config.AnnounceSound = true             -- play chime sound

Custom Emojis

Config.CustomEmojis = {
    { emoji = '🏎️', name = 'race car' },           -- unicode emoji
    { image = 'PepeHands.png', name = 'pepecry' },  -- image emoji
}
INFO

Image files go in the /emojis folder. PNG, JPG, and GIF are supported. Recommended source size is 64x64 or 128x128 — images are auto-scaled to 20x20 in chat. Players select emojis from a picker in the chat input.

Player Settings

Players can customize their chat with /chatsettings:

Config.DefaultSettings = {
    x = 20,               -- pixels from left edge
    y = 20,               -- pixels from top edge
    width = 500,          -- chat window width (px)
    height = 400,         -- chat window height (px)
    visibility = 'fade',  -- 'fade', 'always', 'hidden'
    fadeTimeout = 10,     -- seconds before chat fades
    fontSize = 14,        -- font size (px)
}

Settings are saved per-player in the ngdchat_settings database table.


Staff Commands

CommandDescription
/mute [id]Toggle mute on a player (prevents them from sending messages)
/clearchatClear chat for all players
/staff [message]Send a staff-only message
/announce [message]Broadcast a toast announcement to all players

Staff members can also left-click messages in the chat UI to delete messages or mute players directly.

WARNING

Staff commands require the permission group set in Config.StaffPermission (default: 'command').


Editable Files

FilePurpose
config.luaAll configuration settings and channel definitions
translate.luaAll translatable strings
client/editableclient.luaClient-side hooks
server/editableserver.luaServer-side hooks

Hooks

Hooks let you add custom logic without modifying locked files. They fire automatically at specific points in the chat pipeline.

Server Hooks

Located in server/editableserver.lua:

OnMessageSent

Fires after a message is sent and routed to all recipients. Use this for custom logging, external API calls, or moderation integrations.

function OnMessageSent(source, channel, message, playerName)
    -- source: number — server ID of the sender
    -- channel: string — channel name (e.g., 'global', 'police', 'anon')
    -- message: string — the message text (post-filter, post-sanitize)
    -- playerName: string — real display name, even for anonymous messages
end
INFO

This hook fires regardless of Config.UseDiscordLog. The built-in Discord/FiveManage logging runs separately before this hook. Use this for your own custom integrations.

OnPlayerMuted

Fires when a player is muted or unmuted by staff.

function OnPlayerMuted(source, targetSource, muted)
    -- source: number — server ID of the staff member
    -- targetSource: number — server ID of the muted/unmuted player
    -- muted: boolean — true if muted, false if unmuted
end

OnMessageDeleted

Fires when a staff member deletes a message.

function OnMessageDeleted(source, msgId)
    -- source: number — server ID of the staff member
    -- msgId: number — the message ID that was deleted
end

OnChatCleared

Fires when chat is cleared by a staff member.

function OnChatCleared(source, global)
    -- source: number — server ID of the staff member
    -- global: boolean — true if /clearchat (all players), false if /clear (self only)
end

Client Hooks

Located in client/editableclient.lua:

OnChatOpen

Fires when the player opens chat (T key).

function OnChatOpen()
    -- Use this to hide HUD elements, pause systems, etc.
end

OnChatClose

Fires when the player closes chat (Enter or Escape).

function OnChatClose()
    -- Use this to restore HUD elements, resume systems, etc.
end

OnBubbleShow

Fires when a /me or /do bubble appears above a player's head.

function OnBubbleShow(serverId, channel, text)
    -- serverId: number — server ID of the player with the bubble
    -- channel: string — 'me' or 'do'
    -- text: string — the bubble text
end

ngd-chat | Nemesis Docs