Koja Scripts

Configuration

Every option of Koja Chat in editable/config.lua: general, theme, bubbles, images, binds, developer mode, permissions, departments, channels, commands, anti-spam, filter and logs, plus the editable files.

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

OptionDefaultWhat it does
Config.Locale'en'en or pl (shared/locales.lua)
Config.DebugfalseConsole logs
Config.OpenKey'T'Default key. Players can rebind it in GTA settings, Key Bindings, FiveM
Config.MousetrueShow the cursor while the chat is open (tabs, emoji, settings)
Config.MaxLength256Maximum characters per message
Config.MaxMessages150Messages kept in the UI
Config.FadeOut10Seconds 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.Timestampstruefalse = never show timestamps. When true players can still hide them
Config.HideInPauseMenutrueHide the chat in the pause menu
Config.AllowColorCodesfalseLet 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

editable/config.lua
Config.Theme = {
    style = 'appearance',
    accent = nil,
    playerChoice = true,
}
KeyDescription
styleappearance, velmor, velmor2, grids, modern, emerald, samp, nightshift, discord, twitch, bubble or terminal
accentOverrides the accent colour of the style, for example '#FF9B00'. nil = the colour of the style
playerChoicePlayers can pick style, main colour (24 colour palette), position (3x3 grid) and size in the chat settings

Speech bubbles

editable/config.lua
Config.Bubbles = {
    enabled = true,
    max = 6,
    distance = 18.0,
    duration = 7000,
    showSelf = true,
    channels = { ['local'] = true },
}
KeyDescription
enabledBubbles above heads: an animated ... while typing and messages that type out letter by letter
maxBubbles drawn at once. Each one is a small browser texture
distanceDraw distance in metres
durationMilliseconds a message stays above the head
showSelfShow your own bubble
channelsPlain messages on these channels also get a bubble. Messages from commands with overhead = true always get one

Overhead text and typing indicator

editable/config.lua
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

editable/config.lua
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,
    },
}
KeyDescription
enabledImage previews for links
hostsOnly links from these hosts get a preview, so nobody can log player IPs with their own server
paste.enabledCtrl+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.maxSideLonger side in pixels, scaled down to this
paste.maxKBMaximum size after compression. The server rejects bigger images
paste.cooldownSeconds between images per player. Staff ignore it

The server accepts only PNG, JPEG and WebP data, never URLs, SVG or HTML.

Binds

editable/config.lua
Config.Binds = { enabled = true, max = 20 }

enabled = false turns off /bind, /unbind and /binds. max is the number of binds per player.

Developer mode

editable/config.lua
Config.DevMode = {
    enabled = false,
    ace = 'koja-chat.dev',
    execute = false,
    backlog = 200,
    clientConsole = true,
}
KeyDescription
enabledPlayers with the ACE get a Console tab with the live server console. Errors are red, click a line to copy
aceThe ACE a developer needs
executeAnything typed in the Console tab runs as a server console command. Off by default
backlogLines sent to a developer when they join
clientConsoleAlso 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.

server.cfg
add_ace group.admin koja-chat.dev allow

Permissions

OptionDefaultWhat it does
Config.AdminGroupsnilPassed to KOJA.Server.IsAdmin, for example { 'admin', 'superadmin' }. nil = the koja-lib Config.Admin
Config.AdminAcenilACE 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

editable/config.lua
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 = false

A 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.

editable/config.lua
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 },
}
KeyDescription
idChannel id used by commands and the API
labelA locale key (ch_local) or plain text
color, iconTab colour and icon
rangeProximity in metres. Players further away do not receive the message
adminOnly staff can see and write
jobsOnly these jobs can see and write
departmentSplit by Config.Departments
readonlyPlayers cannot write. Scripts still can
nameMode'character' (character name) or 'player' (FiveM name)
hiddenNo 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:

KeyDescription
channelChannel the text is posted to
styledefault, action, describe, try, twitter, anon or ad
tag, colorLabel and colour of the message
overheadShow text or a bubble above the player
rangeOverrides the range of the channel, in metres
anonymousHide the author. Staff still see the ID
cost, accountMoney taken per message from 'cash' or 'bank'
cooldownSeconds between uses. Staff ignore it
chance/try only: success chance in percent (default 50)
duration/poll only: seconds a poll stays open (default 120)
adminOnly staff can use it
paramsParameter names shown in the suggestions (locale keys such as p_message)
nameUse 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.

editable/config.lua
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 = {},
}
KeyDescription
AntiSpam.intervalMinimum milliseconds between two messages
AntiSpam.burst, windowAt most burst messages in window seconds
AntiSpam.duplicate, duplicateWindowBlocks the same message twice in a row within this many seconds
AntiSpam.autoMuteMutes after strikes blocked messages within a minute of each other, for minutes. nil disables it
Filter.blocktrue rejects the message, false replaces the word
Filter.replaceReplacement character, repeated for each letter
Filter.blockLinksRejects http://, https://, www. and discord.gg
Filter.wordsBlocked 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

editable/config.lua
Config.Logs = {
    enabled = false,
    webhook = '',
    channels = {},
    pm = true,
    reports = true,
    moderation = true,
}
KeyDescription
enabledMaster switch. Logs are sent through KOJA.Server.LogMessage
webhookDefault webhook for everything
channelsPer channel override: a webhook URL, or false to not log that channel. The key is the channel id, 'admin' for example
pm, reportsLog private messages and reports
moderationLog 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

FunctionWhat 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

FunctionWhat 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