From 8bade2ad952db86cbffae6476f85748aaefb5422 Mon Sep 17 00:00:00 2001 From: Joost H <26064691+jhag01@users.noreply.github.com> Date: Sun, 1 Feb 2026 00:45:55 +0100 Subject: [PATCH] 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. --- README.md | 34 +++++++++-- fxmanifest.lua | 14 +++++ main.lua | 5 ++ settings.lua | 41 ++++++++++++++ txAdminLogs/config.lua | 7 --- txAdminLogs/fxmanifest.lua | 14 ----- txAdminLogs/sv_txadmin.lua | 112 ------------------------------------- utils.lua | 62 ++++++++++++++++++++ 8 files changed, 151 insertions(+), 138 deletions(-) create mode 100644 fxmanifest.lua create mode 100644 main.lua create mode 100644 settings.lua delete mode 100644 txAdminLogs/config.lua delete mode 100644 txAdminLogs/fxmanifest.lua delete mode 100644 txAdminLogs/sv_txadmin.lua create mode 100644 utils.lua diff --git a/README.md b/README.md index 9eb8458..d0b6a86 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,32 @@ -# txAdminLogs -Simple Standalone txAdmin Logs made for FiveM +# 🛠️ txAdminLogs +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' + } \ No newline at end of file diff --git a/fxmanifest.lua b/fxmanifest.lua new file mode 100644 index 0000000..088eefb --- /dev/null +++ b/fxmanifest.lua @@ -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' +} \ No newline at end of file diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..aa454f3 --- /dev/null +++ b/main.lua @@ -0,0 +1,5 @@ +for eventName, data in pairs(Settings.Webhooks) do + AddEventHandler('txAdmin:events:' .. eventName, function(eventData) + sendToDiscord(eventName, eventData) + end) +end \ No newline at end of file diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..a7fdc5e --- /dev/null +++ b/settings.lua @@ -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' }, +} \ No newline at end of file diff --git a/txAdminLogs/config.lua b/txAdminLogs/config.lua deleted file mode 100644 index 52f72fe..0000000 --- a/txAdminLogs/config.lua +++ /dev/null @@ -1,7 +0,0 @@ -Config = {} - -Config.txAdminWebhook = 'WEBHOOK' - -Config.Username = 'txAdmin Logs' - -Config.FilterAnnouncements = true \ No newline at end of file diff --git a/txAdminLogs/fxmanifest.lua b/txAdminLogs/fxmanifest.lua deleted file mode 100644 index 34f6f25..0000000 --- a/txAdminLogs/fxmanifest.lua +++ /dev/null @@ -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' -} diff --git a/txAdminLogs/sv_txadmin.lua b/txAdminLogs/sv_txadmin.lua deleted file mode 100644 index f0431ec..0000000 --- a/txAdminLogs/sv_txadmin.lua +++ /dev/null @@ -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 diff --git a/utils.lua b/utils.lua new file mode 100644 index 0000000..5d3f4d8 --- /dev/null +++ b/utils.lua @@ -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**: ()\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 \ No newline at end of file