Refactor txAdminLogs to modular, configurable system

Replaces the old txAdminLogs implementation with a modular, table-driven system. Adds new files (fxmanifest.lua, main.lua, settings.lua, utils.lua) and removes the previous monolithic scripts. Logging is now fully configurable via settings.lua, supports per-event webhooks, and features improved formatting and timestamp handling. Updates README with new installation and configuration instructions.
This commit is contained in:
Joost H
2026-02-01 00:45:55 +01:00
parent 36d6c7b77a
commit 8bade2ad95
8 changed files with 151 additions and 138 deletions

View File

@@ -1,8 +1,32 @@
# txAdminLogs # 🛠️ txAdminLogs
Simple Standalone txAdmin Logs made for FiveM A lightweight, standalone logging system for FiveM that automatically captures and routes **txAdmin** events to Discord.
Drag and drop installation, just add the line below to your server.cfg / resources.cfg ---
`ensure txAdminLogs` ## ✨ Features
* **Zero Maintenance:** Automatically registers listeners for every event defined in your settings.
* **Dual-Routing:** Sends logs to specific category channels (e.g., Bans, Whitelist) while simultaneously maintaining a "Master Log" for full history.
* **Smart Tables:** Automatically flattens complex data (like Player Identifiers) into clean, bulleted lists.
* **Dynamic Timestamps:** Converts expiration Unix codes into human-readable, localized Discord time (e.g., "in 2 days").
* **Standalone:** No framework requirements. Works on any server.
Feel free to modify for personal usage, don't ever distribute it for any amount of money. ---
## 🚀 Installation
1. Drop the `txAdminLogs` folder into your `resources` directory.
2. Open `settings.lua` and replace `'WEBHOOK'` with your actual Discord Webhook URLs.
3. Add `ensure txAdminLogs` to your `server.cfg`.
---
## ⚙️ Configuration
The system uses a simple table structure in `settings.lua`.
* **MasterWebhook:** The catch-all channel.
* **Webhooks Table:**
```lua
['playerBanned'] = {
color = 10038562,
title = '🔨 txAdmin: Player Banned',
webhook = 'YOUR_SPECIFIC_URL'
}

14
fxmanifest.lua Normal file
View File

@@ -0,0 +1,14 @@
fx_version 'cerulean'
game 'gta5'
author 'Joost'
description 'txAdmin Logs'
version '2.0.0'
server_only 'yes'
server_scripts {
'settings.lua',
'utils.lua',
'main.lua'
}

5
main.lua Normal file
View File

@@ -0,0 +1,5 @@
for eventName, data in pairs(Settings.Webhooks) do
AddEventHandler('txAdmin:events:' .. eventName, function(eventData)
sendToDiscord(eventName, eventData)
end)
end

41
settings.lua Normal file
View File

@@ -0,0 +1,41 @@
Settings = {}
Settings.Bot = {
Username = 'txAdmin Logs',
AvatarURL = '',
FooterText = 'txAdmin Logs • ' .. os.date('%Y'),
FooterIcon = '',
}
Settings.Misc = {
IncludeTimestamp = true,
}
Settings.MasterWebhook = 'WEBHOOK'
Settings.Webhooks = {
-- Server-Related Events
['announcement'] = { color = 16776960, title = '📢 txAdmin: Announcement', webhook = 'WEBHOOK' },
['serverShuttingDown'] = { color = 15158332, title = '🛑 txAdmin: Server Shutting Down', webhook = 'WEBHOOK' },
['scheduledRestart'] = { color = 15105570, title = '⏲️ txAdmin: Scheduled Restart', webhook = 'WEBHOOK' },
['scheduledRestartSkipped'] = { color = 3447003, title = '⏭️ txAdmin: Restart Skipped', webhook = 'WEBHOOK' },
-- Player-Related Events
['playerBanned'] = { color = 10038562, title = '🔨 txAdmin: Player Banned', webhook = 'WEBHOOK' },
['playerDirectMessage'] = { color = 16777215, title = '💬 txAdmin: Direct Message', webhook = 'WEBHOOK' },
['playerHealed'] = { color = 3066993, title = '❤️ txAdmin: Player Healed', webhook = 'WEBHOOK' },
['playerKicked'] = { color = 15158332, title = '👢 txAdmin: Player Kicked', webhook = 'WEBHOOK' },
['playerWarned'] = { color = 15844367, title = '⚠️ txAdmin: Player Warned', webhook = 'WEBHOOK' },
-- Whitelist-Related Events
['whitelistPlayer'] = { color = 3066993, title = '📝 txAdmin: Player Whitelisted', webhook = 'WEBHOOK' },
['whitelistPreApproval'] = { color = 3447003, title = '📋 txAdmin: Whitelist Pre-Approval', webhook = 'WEBHOOK' },
['whitelistRequest'] = { color = 1752220, title = '📩 txAdmin: Whitelist Request', webhook = 'WEBHOOK' },
-- Other Events
['actionRevoked'] = { color = 9807270, title = '🔄 txAdmin: Action Revoked', webhook = 'WEBHOOK' },
['adminAuth'] = { color = 15105570, title = '🔐 txAdmin: Admin Authenticated', webhook = 'WEBHOOK' },
['adminsUpdated'] = { color = 3447003, title = '👥 txAdmin: Admins Updated', webhook = 'WEBHOOK' },
['configChanged'] = { color = 15844367, title = '⚙️ txAdmin: Config Changed', webhook = 'WEBHOOK' },
['consoleCommand'] = { color = 0, title = '💻 txAdmin: Console Command', webhook = 'WEBHOOK' },
}

View File

@@ -1,7 +0,0 @@
Config = {}
Config.txAdminWebhook = 'WEBHOOK'
Config.Username = 'txAdmin Logs'
Config.FilterAnnouncements = true

View File

@@ -1,14 +0,0 @@
fx_version 'cerulean'
games { 'gta5' }
author 'Joost#5866'
description 'txAdmin Logs'
version '1.0.5'
server_only "yes"
server_scripts {
'config.lua',
'sv_txadmin.lua'
}

View File

@@ -1,112 +0,0 @@
AddEventHandler('txAdmin:events:playerKicked', function(eventData)
local target = eventData.target
local author = eventData.author
local reason = eventData.reason
sendToDiscord('Player Kicked', "Name: **" .. GetPlayerName(target) .. "** \nAuthor: **" .. author .. "** \nReason: **" .. reason .. "**", 65280)
end)
AddEventHandler('txAdmin:events:playerWarned', function(eventData)
local target = eventData.target
local author = eventData.author
local reason = eventData.reason
local id = eventData.actionId
sendToDiscord('Player Warned', "Name: **" .. GetPlayerName(target) .. "** \nAuthor: **" .. author .. "** \nReason: **" .. reason .. "**\nID: **" .. id .. "**", 65280)
end)
AddEventHandler('txAdmin:events:playerBanned', function(eventData)
local target = eventData.target
local author = eventData.author
local reason = eventData.reason
local id = eventData.actionId
local exp = eventData.expiration
if not exp then
exp = 'Never'
else
exp = os.date('%c', exp)
end
if (type(target) == "table") then
playername = "`Offline Ban`"
else
playername = GetPlayerName(target)
end
sendToDiscord('Player Banned', "Name: **" .. playername .. "** \nAuthor: **" .. author .. "** \nReason: **" .. reason .. "**\nID: **" .. id .. "**\nExpires: **" .. exp .. "**", 65280)
end)
AddEventHandler('txAdmin:events:playerWhitelisted', function(eventData)
local target = eventData.target
local author = eventData.author
local id = eventData.actionId
sendToDiscord('Player Whitelisted', "Identifier: **" .. target .. "** \nAuthor: **" .. author .. "**\nID: **" .. id .. "**", 65280)
end)
AddEventHandler('txAdmin:events:announcement', function(eventData)
local author = eventData.author
local msg = eventData.message
if Config.FilterAnnouncements then
if author ~= 'txAdmin' then
sendToDiscord('Announcement', "Author: **" .. author .. "**\Message: **" .. msg .. "**", 65280)
end
else
sendToDiscord('Announcement', "Author: **" .. author .. "**\Message: **" .. msg .. "**", 65280)
end
end)
AddEventHandler('txAdmin:events:configChanged', function(eventData)
sendToDiscord('Config Changed', "There have been made changes in the txAdmin settings, if this wasn't you then check it asap.", 65280)
end)
AddEventHandler('txAdmin:events:healedPlayer', function(eventData)
local target = eventData.id
if target == -1 then
playername = 'Everyone'
else
playername = GetPlayerName(target)
end
sendToDiscord('Player Healed', "Name: **" .. playername .. "**", 65280)
end)
AddEventHandler('txAdmin:events:serverShuttingDown', function(eventData)
local delay = eventData.delay
local author = eventData.author
local msg = eventData.message
sendToDiscord('Server Shutdown', "Author: **" .. author .. "**\Message: **" .. msg .. "**\Delay: **" .. delay .. "ms**", 65280)
end)
function sendToDiscord(header, message)
local webhook = Config.txAdminWebhook
local name = Config.Username
local connect = {
{
["title"] = header,
["description"] = message
}
}
PerformHttpRequest(webhook, function(err, text, headers) end, 'POST', json.encode({username = name, embeds = connect, avatar_url = avatar}), { ['Content-Type'] = 'application/json' })
end

62
utils.lua Normal file
View File

@@ -0,0 +1,62 @@
function sendToDiscord(logType, message)
local logData = Settings.Webhooks[logType]
local color = logData and logData.color or 8421504
local title = logData and logData.title or "Unknown Log Type"
local targetWebhook = (logData and logData.webhook ~= 'WEBHOOK') and logData.webhook or Settings.MasterWebhook
local formattedDescription = ""
if type(message) == 'table' then
for key, value in pairs(message) do
local label = key:gsub("(%l)(%A)", "%1 %2"):gsub("^%l", string.upper)
if type(value) == 'table' then
local subValues = ""
for _, subVal in pairs(value) do
subValues = subValues .. "" .. tostring(subVal) .. "\n"
end
formattedDescription = formattedDescription .. string.format("**%s**:\n%s", label, subValues)
elseif key:lower() == "expiration" then
if type(value) == "number" and value > 0 then
formattedDescription = formattedDescription .. string.format("**%s**: <t:%s:f> (<t:%s:R>)\n", label, value, value)
elseif value == false or value == 0 then
formattedDescription = formattedDescription .. string.format("**%s**: Permanent\n", label)
else
formattedDescription = formattedDescription .. string.format("**%s**: %s\n", label, tostring(value))
end
else
local valStr = tostring(value)
if valStr ~= "" and valStr ~= "table: 0x..." then
formattedDescription = formattedDescription .. string.format("**%s**: %s\n", label, valStr)
end
end
end
else
formattedDescription = tostring(message)
end
local embedData = {
{
['title'] = title,
['color'] = color,
['footer'] = {
['text'] = Settings.Bot.FooterText,
['icon_url'] = Settings.Bot.FooterIcon,
},
['description'] = formattedDescription,
['timestamp'] = Settings.Misc.IncludeTimestamp and os.date('!%Y-%m-%dT%H:%M:%SZ') or nil,
}
}
local payload = json.encode({
username = Settings.Bot.Username,
avatar_url = Settings.Bot.AvatarURL,
embeds = embedData
})
PerformHttpRequest(targetWebhook, function(err, text, headers) end, 'POST', payload, { ['Content-Type'] = 'application/json' })
if targetWebhook ~= Settings.MasterWebhook then
PerformHttpRequest(Settings.MasterWebhook, function(err, text, headers) end, 'POST', payload, { ['Content-Type'] = 'application/json' })
end
end