Configuration
Options live in editable/config.lua. Server hooks are in editable/server.lua and client hooks in editable/client.lua. Restart the resource after a change.
General
| Option | Default | What it does |
|---|---|---|
Config.Locale | 'en' | en or pl (shared/locales.lua) |
Config.Debug | false | Console logs |
Config.OpenKey | 'T' | Default key. Players can rebind it in GTA settings, Key Bindings, FiveM |
Config.Mouse | true | Show the cursor while the chat is open (tabs, emoji, settings) |
Config.MaxLength | 256 | Maximum characters per message |
Config.MaxMessages | 150 | Messages kept in the UI |
Config.FadeOut | 10 | Seconds after the last message before the feed hides. 0 = never hide |
Config.Position | 'top-left' | 'top-left', 'top-right', 'middle-left' or 'bottom-left' |
Config.Timestamps | true | false = never show timestamps. When true players can still hide them |
Config.HideInPauseMenu | true | Hide the chat in the pause menu |
Config.AllowColorCodes | false | Let players use ^1 to ^9 colour codes |
Config.DefaultChannel | 'local' | Channel for plain text (no command) typed on the All tab or on a tab the player cannot write to |
Theme
Config.Theme = {
style = 'appearance',
accent = nil,
playerChoice = true,
}| Key | Description |
|---|---|
style | appearance, velmor, velmor2, grids, modern, emerald, samp, nightshift, discord, twitch, bubble or terminal |
accent | Overrides the accent colour of the style, for example '#FF9B00'. nil = the colour of the style |
playerChoice | Players can pick style, main colour (24 colour palette), position (3x3 grid) and size in the chat settings |
Speech bubbles
Config.Bubbles = {
enabled = true,
max = 6,
distance = 18.0,
duration = 7000,
showSelf = true,
channels = { ['local'] = true },
}| Key | Description |
|---|---|
enabled | Bubbles above heads: an animated ... while typing and messages that type out letter by letter |
max | Bubbles drawn at once. Each one is a small browser texture |
distance | Draw distance in metres |
duration | Milliseconds a message stays above the head |
showSelf | Show your own bubble |
channels | Plain messages on these channels also get a bubble. Messages from commands with overhead = true always get one |
Overhead text and typing indicator
Config.OverheadText = { enabled = true, duration = 7000, distance = 15.0 }
Config.TypingIndicator = { enabled = true, distance = 12.0 }OverheadText is the plain GTA 3D text for /me, /do and /try. It is used when Config.Bubbles.enabled is false.
TypingIndicator shows ... above players who have the chat open, as a bubble or as 3D text. It uses the player state kojaTyping.
Images
Config.Images = {
enabled = true,
hosts = { 'i.imgur.com', 'cdn.discordapp.com', 'media.discordapp.net', 'media.tenor.com', 'i.ibb.co' },
paste = {
enabled = true,
maxSide = 720,
maxKB = 180,
cooldown = 10,
},
}| Key | Description |
|---|---|
enabled | Image previews for links |
hosts | Only links from these hosts get a preview, so nobody can log player IPs with their own server |
paste.enabled | Ctrl+V a copied image straight into the chat. It is shrunk and compressed in the UI and sent through a latent event, nothing is uploaded anywhere |
paste.maxSide | Longer side in pixels, scaled down to this |
paste.maxKB | Maximum size after compression. The server rejects bigger images |
paste.cooldown | Seconds between images per player. Staff ignore it |
The server accepts only PNG, JPEG and WebP data, never URLs, SVG or HTML.
Binds
Config.Binds = { enabled = true, max = 20 }enabled = false turns off /bind, /unbind and /binds. max is the number of binds per player.
Developer mode
Config.DevMode = {
enabled = false,
ace = 'koja-chat.dev',
execute = false,
backlog = 200,
clientConsole = true,
}| Key | Description |
|---|---|
enabled | Players with the ACE get a Console tab with the live server console. Errors are red, click a line to copy |
ace | The ACE a developer needs |
execute | Anything typed in the Console tab runs as a server console command. Off by default |
backlog | Lines sent to a developer when they join |
clientConsole | Also mirrors your own F8 console into the tab when the client supports it |
The Console tab needs RegisterConsoleListener on the server build. Without it the console prints a warning and the tab only shows the commands. Executed commands are printed to the server console and logged to Discord under console.
add_ace group.admin koja-chat.dev allowPermissions
| Option | Default | What it does |
|---|---|---|
Config.AdminGroups | nil | Passed to KOJA.Server.IsAdmin, for example { 'admin', 'superadmin' }. nil = the koja-lib Config.Admin |
Config.AdminAce | nil | ACE that makes a player staff, for example 'koja-chat.admin' |
Config.StaffBadge | { label = 'STAFF', color = '#ef4444' } | Badge next to staff names on the OOC and Local tabs. nil disables it |
Departments
Config.Departments = {
police = { label = 'LSPD', color = '#3b82f6', jobs = { 'police', 'sheriff' } },
ems = { label = 'EMS', color = '#ef4444', jobs = { 'ambulance' } },
mechanic = { label = 'MECH', color = '#f59e0b', jobs = { 'mechanic' } },
}
Config.DepartmentsRequireDuty = falseA channel with department = true (the /dep command) shows the department label and colour of the player, and players only see messages of their own department. DepartmentsRequireDuty = true puts a player in the department only while on duty. Tabs are refreshed when the job or duty changes on ESX and QBCore.
Channels
Each entry is a tab. The order is the tab order.
Config.Channels = {
{ id = 'local', label = 'ch_local', color = '#a78bfa', icon = 'users', range = 20.0 },
{ id = 'ooc', label = 'ch_ooc', color = '#94a3b8', icon = 'globe', nameMode = 'player' },
{ id = 'social', label = 'ch_social', color = '#38bdf8', icon = 'megaphone' },
{ id = 'dep', label = 'ch_dep', color = '#3b82f6', icon = 'shield', department = true },
{ id = 'admin', label = 'ch_admin', color = '#ef4444', icon = 'crown', admin = true, nameMode = 'player' },
{ id = 'system', label = 'ch_system', color = '#f59e0b', icon = 'bell', readonly = true, hidden = true },
}| Key | Description |
|---|---|
id | Channel id used by commands and the API |
label | A locale key (ch_local) or plain text |
color, icon | Tab colour and icon |
range | Proximity in metres. Players further away do not receive the message |
admin | Only staff can see and write |
jobs | Only these jobs can see and write |
department | Split by Config.Departments |
readonly | Players cannot write. Scripts still can |
nameMode | 'character' (character name) or 'player' (FiveM name) |
hidden | No tab. System messages are shown on every tab anyway |
Icons: users, globe, megaphone, shield, crown, bell, at, mask, mail, flag, info, alert, radio, siren, star, heart, wrench, newspaper, sparkles, clock, mute.
Other resources add channels with RegisterChannel.
Commands
Config.Commands is keyed by command name. Set enabled = false to remove one. A command with a channel posts its text to that channel:
| Key | Description |
|---|---|
channel | Channel the text is posted to |
style | default, action, describe, try, twitter, anon or ad |
tag, color | Label and colour of the message |
overhead | Show text or a bubble above the player |
range | Overrides the range of the channel, in metres |
anonymous | Hide the author. Staff still see the ID |
cost, account | Money taken per message from 'cash' or 'bank' |
cooldown | Seconds between uses. Staff ignore it |
chance | /try only: success chance in percent (default 50) |
duration | /poll only: seconds a poll stays open (default 120) |
admin | Only staff can use it |
params | Parameter names shown in the suggestions (locale keys such as p_message) |
name | Use another command name than the key |
The other commands (pm, r, report, mute, poll and so on) have their own logic in server/commands.lua, and commands with client = true run on the client. See Commands for the defaults.
Moderation
Staff bypass all of it.
Config.AntiSpam = {
enabled = true,
interval = 700,
burst = 5,
window = 8,
duplicate = true,
duplicateWindow = 15,
autoMute = { strikes = 5, minutes = 5 },
}
Config.Filter = {
enabled = true,
block = false,
replace = '*',
blockLinks = false,
words = {},
}| Key | Description |
|---|---|
AntiSpam.interval | Minimum milliseconds between two messages |
AntiSpam.burst, window | At most burst messages in window seconds |
AntiSpam.duplicate, duplicateWindow | Blocks the same message twice in a row within this many seconds |
AntiSpam.autoMute | Mutes after strikes blocked messages within a minute of each other, for minutes. nil disables it |
Filter.block | true rejects the message, false replaces the word |
Filter.replace | Replacement character, repeated for each letter |
Filter.blockLinks | Rejects http://, https://, www. and discord.gg |
Filter.words | Blocked words, case-insensitive, matched anywhere in the text |
/slowmode works even when AntiSpam.enabled is false. Mutes are stored by license in the resource KVP, so they survive reconnects and restarts.
Discord logs
Config.Logs = {
enabled = false,
webhook = '',
channels = {},
pm = true,
reports = true,
moderation = true,
}| Key | Description |
|---|---|
enabled | Master switch. Logs are sent through KOJA.Server.LogMessage |
webhook | Default webhook for everything |
channels | Per channel override: a webhook URL, or false to not log that channel. The key is the channel id, 'admin' for example |
pm, reports | Log private messages and reports |
moderation | Log mutes, unmutes, slow mode, clearing, announcements and deleted messages |
Every entry shows the player, the channel, the character name and the license and Discord identifiers.
Editable files
editable/server.lua
| Function | What it does |
|---|---|
Custom.IsAdmin(src) | Who counts as staff. Default: KOJA.Server.IsAdmin(src, Config.AdminGroups, Config.AdminAce) |
Custom.GetName(src, info, channel) | Name printed next to a message. Default: the character name, or the FiveM name when the channel has nameMode = 'player' |
Custom.GetBadges(src, info, channelId) | Badges before the name, { { label, color } }. Default: the staff badge on OOC and Local |
info is { id, name, player, job, jobLabel, grade, gradeLabel, onDuty, department, admin }.
editable/client.lua
| Function | What it does |
|---|---|
CustomClient.CanOpen() | Return false to block opening (handcuffed, dead, in a cutscene) |
CustomClient.OnOpen() | Called when the chat opens |
CustomClient.OnClose() | Called when the chat closes |