From 035d8af10789d5d0801ea5bd414133e5efa93981 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 13 Apr 2025 23:45:17 +1000 Subject: [PATCH 01/26] Adds a moderation action history into the player submenus without any dependencies using a .json file --- client/gui_c.lua | 53 +++++++++++- fxmanifest.lua | 2 + language/en.json | 12 +++ server/action_history.lua | 168 ++++++++++++++++++++++++++++++++++++++ server/admin_server.lua | 13 ++- server/banlist.lua | 5 +- shared/util_shared.lua | 2 + 7 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 server/action_history.lua diff --git a/client/gui_c.lua b/client/gui_c.lua index 8891d73b..eae9b6ac 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -827,6 +827,57 @@ function GenerateMenu() -- this is a big ass function TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("nodiscordpresent")) end end + + if GetConvar("ea_enableActionHistory", "true") == "true" and permissions["player.actionhistory.view"] then + local actionHistoryMenu = _menuPool:AddSubMenu(thisPlayer, GetLocalisedText("actionhistory"), GetLocalisedText("actionhistoryguide"), true) + actionHistoryMenu:SetMenuWidthOffset(menuWidth) + local loadingItem = NativeUI.CreateItem("Loading...", "Please wait while we fetch the data.") + actionHistoryMenu:AddItem(loadingItem) + TriggerServerEvent("EasyAdmin:GetActionHistory", thePlayer.discord) + + RegisterNetEvent("EasyAdmin:ReceiveActionHistory") + AddEventHandler("EasyAdmin:ReceiveActionHistory", function(actionHistory) + actionHistoryMenu:Clear() + if #actionHistory == 0 then + local noActionsItem = NativeUI.CreateItem(GetLocalisedText("noactions"), GetLocalisedText("noactionsguide")) + actionHistoryMenu:AddItem(noActionsItem) + end + for i, action in ipairs(actionHistory) do + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) + actionSubmenu:SetMenuWidthOffset(menuWidth) + if permissions["player.actionhistory.delete"] then + local actionDelete = NativeUI.CreateItem(GetLocalisedText("deleteaction"), GetLocalisedText("deleteactionguide")) + actionDelete.Activated = function(ParentMenu, SelectedItem) + TriggerServerEvent("EasyAdmin:DeleteAction", action.action, action.discord, action.reason, action.moderatorId) + TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("actiondeleted")) + TriggerServerEvent("EasyAdmin:GetActionHistory", thePlayer.discord) + ParentMenu:Visible(false) + ParentMenu.ParentMenu:Visible(true) + end + actionSubmenu:AddItem(actionDelete) + end + local punishedDiscord = NativeUI.CreateItem(GetLocalisedText("getplayerdiscord"), GetLocalisedText("getplayerdiscordguide")) + punishedDiscord.Activated = function(ParentMenu, SelectedItem) + if action.discord then + copyToClipboard(action.discord) + else + TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("nodiscordpresent")) + end + end + actionSubmenu:AddItem(punishedDiscord) + local moderatorDiscord = NativeUI.CreateItem(GetLocalisedText("getmoderatordiscord"), GetLocalisedText("getmoderatordiscordguide")) + moderatorDiscord.Activated = function(ParentMenu, SelectedItem) + if action.moderatorId then + copyToClipboard(action.moderatorId) + else + TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("nodiscordpresent")) + end + end + actionSubmenu:AddItem(moderatorDiscord) + actionSubmenu:RefreshIndex() + end + end) + end ExecutePluginsFunction("playerMenu", thePlayer.id) @@ -2002,4 +2053,4 @@ function DrawPlayerInfoLoop() end end) -end +end \ No newline at end of file diff --git a/fxmanifest.lua b/fxmanifest.lua index 2d6a741f..f76523f6 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -88,5 +88,7 @@ convar_category 'EasyAdmin' { { "Channel for Discord bot to enable live status", "$ea_botStatusChannel", "CV_STRING", "true" }, { "Enable Allowlist", "$ea_enableAllowlist", "CV_BOOL", "false" }, { "Routing Bucket Options", "$ea_routingBucketOptions", "CV_BOOL", "false" }, + { "Enable Action History", "$ea_enableActionHistory", "CV_BOOL", "true" }, + { "Action History Expiry", "$ea_actionHistoryExpiry", "CV_INT", "30"}, -- Recommended time is 30 days } } \ No newline at end of file diff --git a/language/en.json b/language/en.json index 5ecf011c..47a0f736 100644 --- a/language/en.json +++ b/language/en.json @@ -115,6 +115,18 @@ "bucketguide": "Forces either player or yourself into the other's routing bucket.", "playerbucketjoined": "Joined player bucket.", "playerbucketforced": "Player has been forced to your bucket.", + + "actionhistory": "Action History", + "actionhistoryguide": "View moderation actions against this user.", + "noactions": "No actions found.", + "noactionsguide": "No past moderation actions for this user.", + "deleteaction": "Delete Action", + "deleteactionguide": "Delete this action from the history.", + "actiondeleted": "Action deleted.", + "getplayerdiscord": "Copy Discord of Player", + "getplayerdiscordguide": "Copy the Discord ID of the Player.", + "getmoderatordiscord": "Copy Discord of Moderator", + "getmoderatordiscordguide": "Copy the Discord ID of the Moderator.", "copydiscord": "Copy Discord ID", "discordcopied": "Discord ID copied to clipboard.", diff --git a/server/action_history.lua b/server/action_history.lua new file mode 100644 index 00000000..06021905 --- /dev/null +++ b/server/action_history.lua @@ -0,0 +1,168 @@ +------------------------------------ +------------------------------------ +---- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING +---- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING +---- +---- +---- If you are a developer and want to change something, consider writing a plugin instead: +---- https://easyadmin.readthedocs.io/en/latest/plugins/ +---- +------------------------------------ +------------------------------------ + +local actions = {} + +local function SaveActions(actions) + +end + +RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) + if DoesPlayerHavePermission(source, "player.actionhistory.view") then + if not discordId then + PrintDebugMessage("No Discord ID provided, returning empty action history.", 2) + TriggerClientEvent("EasyAdmin:ReceiveActionHistory", source, {}) + return + end + local history = {} + if actions then + for i, action in ipairs(actions) do + if tostring(action.discord) == tostring(discordId) then + table.insert(history, { + action = action.action, + reason = action.reason, + discord = action.discord, + moderator = action.moderator, + moderatorId = action.moderatorId, + time = action.time, + }) + end + end + else + PrintDebugMessage("No actions found in the history.", 2) + end + TriggerClientEvent("EasyAdmin:ReceiveActionHistory", source, history) + else + PrintDebugMessage("Player does not have permission to view action history.", 2) + TriggerClientEvent("EasyAdmin:ReceiveActionHistory", source, {}) + end +end) + +RegisterNetEvent("EasyAdmin:DeleteAction", function(action, discordId, reason, moderatorId) + if DoesPlayerHavePermission(source, "player.actionhistory.delete") then + print(action, discordId, reason, moderatorId) + if not action or not discordId or not reason or not moderatorId then + print("Invalid parameters provided for action deletion.") + PrintDebugMessage("Invalid parameters provided for action deletion.", 2) + return + end + for i, act in ipairs(actions) do + if act.action == action and act.discord == discordId and act.reason == reason and act.moderatorId == moderatorId then + table.remove(actions, i) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + PrintDebugMessage("Removed action: " .. json.encode(act), 4) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + end + PrintDebugMessage("No matching action found for deletion.", 2) + else + PrintDebugMessage("Player does not have permission to delete actions.", 2) + end +end) + +AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) + local change = (forceChange or false) + local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") + if not content then + PrintDebugMessage("actions.json file was missing, we created a new one.", 2) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + content = json.encode({}) + end + actions = json.decode(content) + + if not actions then + PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") + PrintDebugMessage("^1Failed^7 to load Actions!\n") + PrintDebugMessage("Please check your actions file for errors, ^Action history *will not* work!^7\n") + PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") + return + end + + if data and not remove then + if data.action == "ban" then + table.insert(actions, { + time = os.time(), + action = "Ban", + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + expire = data.expire, + expireString = data.expireString + }) + elseif data.action == "kick" then + table.insert(actions, { + time = os.time(), + action = "Kick", + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + }) + elseif data.action == "warn" then + table.insert(actions, { + time = os.time(), + action = "Warn", + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + }) + elseif data.action == "unban" then + return + end + PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) + change=true + elseif not data then + return + end + if data and remove then + PrintDebugMessage("Removed the following data from actions:\n"..table_to_string(data), 4) + change = true + end + if change then + PrintDebugMessage("Actions changed, saving..", 4) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + end + PrintDebugMessage("Completed Actions Updated.", 4) +end) + +for i, action in ipairs(actions) do + if action.time + (GetConvar("ea_actionHistoryExpiry", 30) * 24 * 60 * 60) < os.time() then + table.remove(actions, i) + PrintDebugMessage("Removed expired action: " .. json.encode(action), 4) + end +end + +local change = (forceChange or false) +local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") +if not content then + PrintDebugMessage("actions.json file was missing, we created a new one.", 2) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + content = json.encode({}) +end +actions = json.decode(content) \ No newline at end of file diff --git a/server/admin_server.lua b/server/admin_server.lua index 21173411..8ac8c1b7 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -314,6 +314,14 @@ Citizen.CreateThread(function() reason = formatShortcuts(reason) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(source, false, true), getName(playerId, true, true), reason), "kick", 16711680) PrintDebugMessage("Kicking Player "..getName(source, true).." for "..reason, 3) + if GetConvar("ea_enableActionHistory", "true") == "true" then + local playerDiscord = GetPlayerIdentifierByType(playerId, 'discord') + local discordId + if playerDiscord then + discordId = playerDiscord:match("discord:(%d+)") + TriggerEvent("EasyAdmin:LogAction", { action = "kick", license = discordId, reason = reason, banner = getName(source, true, true)}) + end + end DropPlayer(playerId, string.format(GetLocalisedText("kicked"), getName(source), reason) ) elseif CachedPlayers[playerId].immune then TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) @@ -757,7 +765,7 @@ Citizen.CreateThread(function() RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason) local src = source - if DoesPlayerHavePermission(src,"player.warn") and not CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then + if DoesPlayerHavePermission(src,"player.warn") and CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then SetAdminCooldown(source, "warn") reason = formatShortcuts(reason) local maxWarnings = GetConvarInt("ea_maxWarnings", 3) @@ -771,6 +779,9 @@ Citizen.CreateThread(function() }) TriggerClientEvent("txcl:showWarning", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", { action = "warn", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + end if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) diff --git a/server/banlist.lua b/server/banlist.lua index bbb1474a..582d62bd 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -28,10 +28,13 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires) } + local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "ban", time = os.time() } updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", ban) + end DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) elseif CachedPlayers[playerId].immune then TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) diff --git a/shared/util_shared.lua b/shared/util_shared.lua index fde66506..c63d3756 100644 --- a/shared/util_shared.lua +++ b/shared/util_shared.lua @@ -14,6 +14,8 @@ permissions = { ["player.screenshot"] = false, ["player.mute"] = false, ["player.warn"] = false, + ["player.actionhistory.view"] = false, + ["player.actionhistory.delete"] = false, ["player.teleport.everyone"] = false, ["player.reports.view"] = false, ["player.reports.claim"] = false, From aa3e54301794cb9fb087935934cbfeb846d19555 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 13 Apr 2025 23:59:46 +1000 Subject: [PATCH 02/26] Refactor action deletion logic to use action ID and improve action history display format --- client/gui_c.lua | 4 ++-- server/action_history.lua | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index eae9b6ac..50396597 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -843,12 +843,12 @@ function GenerateMenu() -- this is a big ass function actionHistoryMenu:AddItem(noActionsItem) end for i, action in ipairs(actionHistory) do - local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "["..action.id.."] " .. action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) if permissions["player.actionhistory.delete"] then local actionDelete = NativeUI.CreateItem(GetLocalisedText("deleteaction"), GetLocalisedText("deleteactionguide")) actionDelete.Activated = function(ParentMenu, SelectedItem) - TriggerServerEvent("EasyAdmin:DeleteAction", action.action, action.discord, action.reason, action.moderatorId) + TriggerServerEvent("EasyAdmin:DeleteAction", action.id) TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("actiondeleted")) TriggerServerEvent("EasyAdmin:GetActionHistory", thePlayer.discord) ParentMenu:Visible(false) diff --git a/server/action_history.lua b/server/action_history.lua index 06021905..15bc2404 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -47,16 +47,14 @@ RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) end end) -RegisterNetEvent("EasyAdmin:DeleteAction", function(action, discordId, reason, moderatorId) +RegisterNetEvent("EasyAdmin:DeleteAction", function(actionId) if DoesPlayerHavePermission(source, "player.actionhistory.delete") then - print(action, discordId, reason, moderatorId) - if not action or not discordId or not reason or not moderatorId then - print("Invalid parameters provided for action deletion.") + if not actionId then PrintDebugMessage("Invalid parameters provided for action deletion.", 2) return end for i, act in ipairs(actions) do - if act.action == action and act.discord == discordId and act.reason == reason and act.moderatorId == moderatorId then + if act.id == actionId then table.remove(actions, i) local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) if not saved then @@ -100,6 +98,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) if data.action == "ban" then table.insert(actions, { time = os.time(), + id = #actions + 1, action = "Ban", discord = data.discord, reason = data.reason, @@ -111,6 +110,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) elseif data.action == "kick" then table.insert(actions, { time = os.time(), + id = #actions + 1, action = "Kick", discord = data.discord, reason = data.reason, @@ -120,6 +120,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) elseif data.action == "warn" then table.insert(actions, { time = os.time(), + id = #actions + 1, action = "Warn", discord = data.discord, reason = data.reason, From 501331398e5265075426541398febcbe54f7e341 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 14 Apr 2025 00:25:12 +1000 Subject: [PATCH 03/26] Enhance action logging by adding detailed ban and unban information to action history --- server/action_history.lua | 27 +++++++++++++++++++++++---- server/banlist.lua | 8 +++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/server/action_history.lua b/server/action_history.lua index 15bc2404..d9b21637 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -99,7 +99,21 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) table.insert(actions, { time = os.time(), id = #actions + 1, - action = "Ban", + banId = data.banId, + action = "BAN", + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + expire = data.expire, + expireString = data.expireString + }) + elseif data.action == "offline ban" then + table.insert(actions, { + time = os.time(), + id = #actions + 1, + banId = data.banId, + action = "OFFLINE BAN", discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -111,7 +125,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) table.insert(actions, { time = os.time(), id = #actions + 1, - action = "Kick", + action = "KICK", discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -121,14 +135,19 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) table.insert(actions, { time = os.time(), id = #actions + 1, - action = "Warn", + action = "WARN", discord = data.discord, reason = data.reason, moderator = data.moderator, moderatorId = data.moderatorId, }) elseif data.action == "unban" then - return + for i, act in ipairs(actions) do + if act.banId == data.banId then + act["action"] = "UNBAN" + break + end + end end PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) change=true diff --git a/server/banlist.lua b/server/banlist.lua index 582d62bd..12a8383b 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -58,8 +58,11 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires } + local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "offline ban", time = os.time() } updateBlacklist( ban ) + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", ban) + end PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminofflinebannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ) ), "ban", 16711680) end @@ -417,6 +420,9 @@ function UnbanId(id) if GetConvar("ea_custombanlist", "false") == "true" then TriggerEvent("ea_data:removeBan", ban) end + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", {action = "unban", banId = id }) + end end end end From 3305aaf00f383d789b5b9e34c7f0be32ee36d3aa Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 14 Apr 2025 00:29:29 +1000 Subject: [PATCH 04/26] Normalize action names in logging: use consistent casing for actions (BAN, KICK, WARN, UNBAN) across the action history --- server/action_history.lua | 10 +++++----- server/admin_server.lua | 10 +++++++--- server/banlist.lua | 6 +++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/server/action_history.lua b/server/action_history.lua index d9b21637..fe018079 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -100,7 +100,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) time = os.time(), id = #actions + 1, banId = data.banId, - action = "BAN", + action = data.action, discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -113,7 +113,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) time = os.time(), id = #actions + 1, banId = data.banId, - action = "OFFLINE BAN", + action = data.action, discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -125,7 +125,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) table.insert(actions, { time = os.time(), id = #actions + 1, - action = "KICK", + action = data.action, discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -135,7 +135,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) table.insert(actions, { time = os.time(), id = #actions + 1, - action = "WARN", + action = data.action, discord = data.discord, reason = data.reason, moderator = data.moderator, @@ -144,7 +144,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) elseif data.action == "unban" then for i, act in ipairs(actions) do if act.banId == data.banId then - act["action"] = "UNBAN" + act["action"] = data.action break end end diff --git a/server/admin_server.lua b/server/admin_server.lua index 8ac8c1b7..01ecd576 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -780,13 +780,16 @@ Citizen.CreateThread(function() TriggerClientEvent("txcl:showWarning", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", { action = "warn", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) end if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) DropPlayer(id, GetLocalisedText("warnkicked")) WarnedPlayers[id] = nil + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", {action = "KICK", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) + end elseif GetConvar("ea_warnAction", "kick") == "ban" then local bannedIdentifiers = CachedPlayers[id].identifiers or getAllPlayerIdentifiers(id) local bannedUsername = CachedPlayers[id].name or getName(id, true) @@ -795,8 +798,9 @@ Citizen.CreateThread(function() reason = GetLocalisedText("warnbanned").. string.format(GetLocalisedText("reasonadd"), CachedPlayers[id].name, getName(source, true) ) local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires } updateBlacklist( ban ) - - + if GetConvar("ea_enableActionHistory", "true") == "true" then + TriggerEvent("EasyAdmin:LogAction", {action = "BAN", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) + end PrintDebugMessage("Player "..getName(source,true).." warnbanned player "..CachedPlayers[id].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), bannedUsername, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) diff --git a/server/banlist.lua b/server/banlist.lua index 12a8383b..e860c015 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -28,7 +28,7 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "ban", time = os.time() } + local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) @@ -58,7 +58,7 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "offline ban", time = os.time() } + local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } updateBlacklist( ban ) if GetConvar("ea_enableActionHistory", "true") == "true" then TriggerEvent("EasyAdmin:LogAction", ban) @@ -421,7 +421,7 @@ function UnbanId(id) TriggerEvent("ea_data:removeBan", ban) end if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", {action = "unban", banId = id }) + TriggerEvent("EasyAdmin:LogAction", {action = "UNBAN", banId = id }) end end end From 0c02a695b0e0d2dc9a759549808469a57a161d61 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:06:10 +1000 Subject: [PATCH 05/26] Add unban option to action history for banned players --- client/gui_c.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/gui_c.lua b/client/gui_c.lua index 50396597..80172f41 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -845,6 +845,17 @@ function GenerateMenu() -- this is a big ass function for i, action in ipairs(actionHistory) do local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "["..action.id.."] " .. action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) + if action.action == "BAN" and permissions["player.ban.remove"] then + local actionUnban = NativeUI.CreateItem(GetLocalisedText("unbanplayer"), GetLocalisedText("unbanplayerguide")) + actionUnban.Activated = function(ParentMenu, SelectedItem) + TriggerServerEvent("EasyAdmin:UnbanPlayer", action.id) + TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("unbanplayer")) + TriggerServerEvent("EasyAdmin:GetActionHistory", thePlayer.discord) + ParentMenu:Visible(false) + ParentMenu.ParentMenu:Visible(true) + end + actionSubmenu:AddItem(actionUnban) + end if permissions["player.actionhistory.delete"] then local actionDelete = NativeUI.CreateItem(GetLocalisedText("deleteaction"), GetLocalisedText("deleteactionguide")) actionDelete.Activated = function(ParentMenu, SelectedItem) From 4fb2aaf65cce71a0784679687d4f43ca11c15daf Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Tue, 15 Apr 2025 21:19:50 +1000 Subject: [PATCH 06/26] Implement ban storage functionality and integrate ban addition in offline ban event --- client/gui_c.lua | 2 +- server/action_history.lua | 17 ++++++----------- server/admin_server.lua | 3 ++- server/banlist.lua | 6 ++++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index 50396597..819a365e 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -843,7 +843,7 @@ function GenerateMenu() -- this is a big ass function actionHistoryMenu:AddItem(noActionsItem) end for i, action in ipairs(actionHistory) do - local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "["..action.id.."] " .. action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) if permissions["player.actionhistory.delete"] then local actionDelete = NativeUI.CreateItem(GetLocalisedText("deleteaction"), GetLocalisedText("deleteactionguide")) diff --git a/server/action_history.lua b/server/action_history.lua index fe018079..c0daaaa7 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -10,12 +10,6 @@ ------------------------------------ ------------------------------------ -local actions = {} - -local function SaveActions(actions) - -end - RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) if DoesPlayerHavePermission(source, "player.actionhistory.view") then if not discordId then @@ -28,6 +22,7 @@ RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) for i, action in ipairs(actions) do if tostring(action.discord) == tostring(discordId) then table.insert(history, { + id = action.id, action = action.action, reason = action.reason, discord = action.discord, @@ -95,7 +90,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) end if data and not remove then - if data.action == "ban" then + if data.action == "BAN" then table.insert(actions, { time = os.time(), id = #actions + 1, @@ -108,7 +103,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) expire = data.expire, expireString = data.expireString }) - elseif data.action == "offline ban" then + elseif data.action == "OFFLINE BAN" then table.insert(actions, { time = os.time(), id = #actions + 1, @@ -121,7 +116,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) expire = data.expire, expireString = data.expireString }) - elseif data.action == "kick" then + elseif data.action == "KICK" then table.insert(actions, { time = os.time(), id = #actions + 1, @@ -131,7 +126,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) moderator = data.moderator, moderatorId = data.moderatorId, }) - elseif data.action == "warn" then + elseif data.action == "WARN" then table.insert(actions, { time = os.time(), id = #actions + 1, @@ -141,7 +136,7 @@ AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) moderator = data.moderator, moderatorId = data.moderatorId, }) - elseif data.action == "unban" then + elseif data.action == "UNBAN" then for i, act in ipairs(actions) do if act.banId == data.banId then act["action"] = data.action diff --git a/server/admin_server.lua b/server/admin_server.lua index 01ecd576..09c21c88 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -765,7 +765,7 @@ Citizen.CreateThread(function() RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason) local src = source - if DoesPlayerHavePermission(src,"player.warn") and CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then + if DoesPlayerHavePermission(src,"player.warn") and not CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then SetAdminCooldown(source, "warn") reason = formatShortcuts(reason) local maxWarnings = GetConvarInt("ea_maxWarnings", 3) @@ -780,6 +780,7 @@ Citizen.CreateThread(function() TriggerClientEvent("txcl:showWarning", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) if GetConvar("ea_enableActionHistory", "true") == "true" then + -- Storage:addAction({ action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) end if WarnedPlayers[id].warns >= maxWarnings then diff --git a/server/banlist.lua b/server/banlist.lua index e860c015..f6aa6c25 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -29,7 +29,8 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } - updateBlacklist( ban ) + Storage:addBan(ban) + -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) if GetConvar("ea_enableActionHistory", "true") == "true" then @@ -59,7 +60,8 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } - updateBlacklist( ban ) + Storage.addBan(ban) + -- updateBlacklist( ban ) if GetConvar("ea_enableActionHistory", "true") == "true" then TriggerEvent("EasyAdmin:LogAction", ban) end From d1481ec100528bb92fd0ba38daf49a7cc0664f44 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:10:37 +1000 Subject: [PATCH 07/26] Update loading item text in action history menu for better localization --- client/gui_c.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index 571d0e6b..b2b45398 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -831,7 +831,7 @@ function GenerateMenu() -- this is a big ass function if GetConvar("ea_enableActionHistory", "true") == "true" and permissions["player.actionhistory.view"] then local actionHistoryMenu = _menuPool:AddSubMenu(thisPlayer, GetLocalisedText("actionhistory"), GetLocalisedText("actionhistoryguide"), true) actionHistoryMenu:SetMenuWidthOffset(menuWidth) - local loadingItem = NativeUI.CreateItem("Loading...", "Please wait while we fetch the data.") + local loadingItem = NativeUI.CreateItem(GetLocalisedText("actionsloading"), GetLocalisedText("actionsloadingguide")) actionHistoryMenu:AddItem(loadingItem) TriggerServerEvent("EasyAdmin:GetActionHistory", thePlayer.discord) From 36805088954ed09703e2bb3623367ced26432dc8 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:14:25 +1000 Subject: [PATCH 08/26] Refactor action logging to ensure action history is recorded consistently, regardless of action history setting --- server/action_history.lua | 170 +++++++++++++++++++------------------- server/admin_server.lua | 14 +--- server/banlist.lua | 12 +-- 3 files changed, 93 insertions(+), 103 deletions(-) diff --git a/server/action_history.lua b/server/action_history.lua index c0daaaa7..1154c410 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -69,98 +69,100 @@ RegisterNetEvent("EasyAdmin:DeleteAction", function(actionId) end) AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) - local change = (forceChange or false) - local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") - if not content then - PrintDebugMessage("actions.json file was missing, we created a new one.", 2) - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + if GetConvar("ea_enableActionHistory", "true") == "true" then + local change = (forceChange or false) + local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") + if not content then + PrintDebugMessage("actions.json file was missing, we created a new one.", 2) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + content = json.encode({}) end - content = json.encode({}) - end - actions = json.decode(content) + actions = json.decode(content) - if not actions then - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - PrintDebugMessage("^1Failed^7 to load Actions!\n") - PrintDebugMessage("Please check your actions file for errors, ^Action history *will not* work!^7\n") - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - return - end + if not actions then + PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") + PrintDebugMessage("^1Failed^7 to load Actions!\n") + PrintDebugMessage("Please check your actions file for errors, ^Action history *will not* work!^7\n") + PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") + return + end - if data and not remove then - if data.action == "BAN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - banId = data.banId, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - expire = data.expire, - expireString = data.expireString - }) - elseif data.action == "OFFLINE BAN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - banId = data.banId, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - expire = data.expire, - expireString = data.expireString - }) - elseif data.action == "KICK" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - }) - elseif data.action == "WARN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - }) - elseif data.action == "UNBAN" then - for i, act in ipairs(actions) do - if act.banId == data.banId then - act["action"] = data.action - break + if data and not remove then + if data.action == "BAN" then + table.insert(actions, { + time = os.time(), + id = #actions + 1, + banId = data.banId, + action = data.action, + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + expire = data.expire, + expireString = data.expireString + }) + elseif data.action == "OFFLINE BAN" then + table.insert(actions, { + time = os.time(), + id = #actions + 1, + banId = data.banId, + action = data.action, + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + expire = data.expire, + expireString = data.expireString + }) + elseif data.action == "KICK" then + table.insert(actions, { + time = os.time(), + id = #actions + 1, + action = data.action, + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + }) + elseif data.action == "WARN" then + table.insert(actions, { + time = os.time(), + id = #actions + 1, + action = data.action, + discord = data.discord, + reason = data.reason, + moderator = data.moderator, + moderatorId = data.moderatorId, + }) + elseif data.action == "UNBAN" then + for i, act in ipairs(actions) do + if act.banId == data.banId then + act["action"] = data.action + break + end end end + PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) + change=true + elseif not data then + return end - PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) - change=true - elseif not data then - return - end - if data and remove then - PrintDebugMessage("Removed the following data from actions:\n"..table_to_string(data), 4) - change = true - end - if change then - PrintDebugMessage("Actions changed, saving..", 4) - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + if data and remove then + PrintDebugMessage("Removed the following data from actions:\n"..table_to_string(data), 4) + change = true + end + if change then + PrintDebugMessage("Actions changed, saving..", 4) + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end end + PrintDebugMessage("Completed Actions Updated.", 4) end - PrintDebugMessage("Completed Actions Updated.", 4) end) for i, action in ipairs(actions) do diff --git a/server/admin_server.lua b/server/admin_server.lua index 09c21c88..f8ccc1b4 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -779,18 +779,14 @@ Citizen.CreateThread(function() }) TriggerClientEvent("txcl:showWarning", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) - if GetConvar("ea_enableActionHistory", "true") == "true" then - -- Storage:addAction({ action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) - TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) - end + -- Storage:addAction({ action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) DropPlayer(id, GetLocalisedText("warnkicked")) WarnedPlayers[id] = nil - if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", {action = "KICK", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) - end + TriggerEvent("EasyAdmin:LogAction", {action = "KICK", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) elseif GetConvar("ea_warnAction", "kick") == "ban" then local bannedIdentifiers = CachedPlayers[id].identifiers or getAllPlayerIdentifiers(id) local bannedUsername = CachedPlayers[id].name or getName(id, true) @@ -799,9 +795,7 @@ Citizen.CreateThread(function() reason = GetLocalisedText("warnbanned").. string.format(GetLocalisedText("reasonadd"), CachedPlayers[id].name, getName(source, true) ) local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires } updateBlacklist( ban ) - if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", {action = "BAN", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) - end + TriggerEvent("EasyAdmin:LogAction", {action = "BAN", discord = CachedPlayers[id].discord, reason = "Reached maximum warnings", moderator = "Server", moderatorId = 0}) PrintDebugMessage("Player "..getName(source,true).." warnbanned player "..CachedPlayers[id].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), bannedUsername, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) diff --git a/server/banlist.lua b/server/banlist.lua index f6aa6c25..f4d5bf69 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -33,9 +33,7 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) - if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", ban) - end + TriggerEvent("EasyAdmin:LogAction", ban) DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) elseif CachedPlayers[playerId].immune then TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) @@ -62,9 +60,7 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } Storage.addBan(ban) -- updateBlacklist( ban ) - if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", ban) - end + TriggerEvent("EasyAdmin:LogAction", ban) PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminofflinebannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ) ), "ban", 16711680) end @@ -422,9 +418,7 @@ function UnbanId(id) if GetConvar("ea_custombanlist", "false") == "true" then TriggerEvent("ea_data:removeBan", ban) end - if GetConvar("ea_enableActionHistory", "true") == "true" then - TriggerEvent("EasyAdmin:LogAction", {action = "UNBAN", banId = id }) - end + TriggerEvent("EasyAdmin:LogAction", {action = "UNBAN", banId = id }) end end end From 76355de716c034908a64c07f0f2dd001cb4a9707 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:16:20 +1000 Subject: [PATCH 09/26] Update English localization for improved clarity and consistency --- language/en.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/language/en.json b/language/en.json index 47a0f736..125a7801 100644 --- a/language/en.json +++ b/language/en.json @@ -118,6 +118,8 @@ "actionhistory": "Action History", "actionhistoryguide": "View moderation actions against this user.", + "actionsloading": "Loading...", + "actionsloadingguide": "Please wait while we fetch the data.", "noactions": "No actions found.", "noactionsguide": "No past moderation actions for this user.", "deleteaction": "Delete Action", From 989b2c7db1758c0806a829ca0a4529b6cae7a8ff Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:30:02 +1000 Subject: [PATCH 10/26] feat(storage): implement banlist and actions management functions --- server/storage.lua | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 server/storage.lua diff --git a/server/storage.lua b/server/storage.lua new file mode 100644 index 00000000..9de6cb93 --- /dev/null +++ b/server/storage.lua @@ -0,0 +1,89 @@ +local banlist = {} +local actions = {} + +local function SaveList(list, fileName) + local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") + if not content then + PrintDebugMessage(fileName .. ".json file was missing, we created a new one.", 2) + content = json.encode({}) + end + + local saved = SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode(list, {indent = True}), -1) + if not saved then + PrintDebugMessage("^1Saving " .. fileName .. ".json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end +end + +local function LoadList(fileName) + local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") + if content then + return json.decode(content) + else + SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode({}), -1) + return {} + end +end + +banlist = LoadList("banlist") +actions = LoadList("actions") + +Storage = { + get = function(list, type, input) + for i, item in ipairs(list) do + if item[type] == input then + return item + end + end + return nil + end, + add = function(list, input, fileName) + table.insert(list, input) + SaveList(list, fileName) + end, + remove = function(list, type, input, fileName) + for i, item in ipairs(list) do + if item[type] == input then + table.remove(list, i) + SaveList(list, fileName) + end + end + return + end, + getBan = function(banId) + for i, ban in ipairs(banlist) do + if ban.banId == banId then + return ban + end + end + return nil + end, + addBan = function(ban) + table.insert(banlist, ban) + SaveList(banlist, "banlist") + end, + removeBan = function(banId) + for i, ban in ipairs(banlist) do + if ban.banId == banId then + table.remove(banlist, i) + SaveList(banlist, "banlist") + end + end + return + end, + addAction = function(data) + print("Adding action...") + print(json.encode(data)) + table.insert(actions, data) + SaveList(actions, "actions") + end, + removeAction = function(data) + for i, act in ipairs(actions) do + if act.id == data.id then + table.remove(actions, i) + SaveList(actions, "actions") + end + end + return + end, + apiVersion = 1, +} \ No newline at end of file From 73e1691612f9ad738b54c7c444fb32a5b624c22a Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:30:08 +1000 Subject: [PATCH 11/26] feat(storage): refactor ban and action management functions for improved clarity and performance --- fxmanifest.lua | 1 + server/action_history.lua | 2 + server/admin_server.lua | 9 +-- server/banlist.lua | 12 ++-- server/storage.lua | 112 +++++++++++++++++++++++++++----------- 5 files changed, 95 insertions(+), 41 deletions(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index f76523f6..fde280d0 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -16,6 +16,7 @@ node_version '22' shared_script 'shared/util_shared.lua' server_scripts { + "server/storage.lua", "server/*.lua", "dist/*.js", "plugins/**/*_shared.lua", diff --git a/server/action_history.lua b/server/action_history.lua index 1154c410..73658f89 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -10,6 +10,8 @@ ------------------------------------ ------------------------------------ +local actions = {} + RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) if DoesPlayerHavePermission(source, "player.actionhistory.view") then if not discordId then diff --git a/server/admin_server.lua b/server/admin_server.lua index f8ccc1b4..3c9df9e0 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -10,7 +10,6 @@ ------------------------------------ ------------------------------------ - -- Cooldowns for Admin Actions AdminCooldowns = {} -- Returns true if allowed, false if cooldown active @@ -765,7 +764,7 @@ Citizen.CreateThread(function() RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason) local src = source - if DoesPlayerHavePermission(src,"player.warn") and not CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then + if DoesPlayerHavePermission(src,"player.warn") and CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then SetAdminCooldown(source, "warn") reason = formatShortcuts(reason) local maxWarnings = GetConvarInt("ea_maxWarnings", 3) @@ -779,8 +778,10 @@ Citizen.CreateThread(function() }) TriggerClientEvent("txcl:showWarning", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) - -- Storage:addAction({ action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) - TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + local test_name = getName(source, true, false) + -- local warn = { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = test_name, moderatorId = CachedPlayers[source].discord } + Storage.addAction("WARN", CachedPlayers[id].discord, reason, test_name, CachedPlayers[source].discord) + -- TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) diff --git a/server/banlist.lua b/server/banlist.lua index f4d5bf69..bc4ef267 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -29,11 +29,10 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } - Storage:addBan(ban) + Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) - TriggerEvent("EasyAdmin:LogAction", ban) DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) elseif CachedPlayers[playerId].immune then TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) @@ -57,8 +56,8 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } - Storage.addBan(ban) + --local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } + Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "OFFLINE BAN", os.time()) -- updateBlacklist( ban ) TriggerEvent("EasyAdmin:LogAction", ban) PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) @@ -101,8 +100,9 @@ function addBanExport(playerId,reason,expires,banner) expires = 10444633200 end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), getName(tostring(playerId) or "?"), banner or "Unknown" ) - local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = banner or "Unknown", reason = reason, expire = expires, expireString = formatDateString(expires) } - updateBlacklist( ban ) + -- local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = banner or "Unknown", reason = reason, expire = expires, expireString = formatDateString(expires) } + -- updateBlacklist( ban ) + Storage.addBan(GetFreshBanId(), bannedUsername, bannedIdentifiers, banner or "Unknown", reason, expires, formatDateString(expires), "BAN", os.time()) if source then PrintDebugMessage("Player "..getName(source,true).." added ban "..reason, 3) diff --git a/server/storage.lua b/server/storage.lua index 9de6cb93..587e6c47 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -28,27 +28,27 @@ banlist = LoadList("banlist") actions = LoadList("actions") Storage = { - get = function(list, type, input) - for i, item in ipairs(list) do - if item[type] == input then - return item - end - end - return nil - end, - add = function(list, input, fileName) - table.insert(list, input) - SaveList(list, fileName) - end, - remove = function(list, type, input, fileName) - for i, item in ipairs(list) do - if item[type] == input then - table.remove(list, i) - SaveList(list, fileName) - end - end - return - end, + -- get = function(list, type, input) + -- for i, item in ipairs(list) do + -- if item[type] == input then + -- return item + -- end + -- end + -- return nil + -- end, + -- add = function(list, input, fileName) + -- table.insert(list, input) + -- SaveList(list, fileName) + -- end, + -- remove = function(list, type, input, fileName) + -- for i, item in ipairs(list) do + -- if item[type] == input then + -- table.remove(list, i) + -- SaveList(list, fileName) + -- end + -- end + -- return + -- end, getBan = function(banId) for i, ban in ipairs(banlist) do if ban.banId == banId then @@ -57,30 +57,80 @@ Storage = { end return nil end, - addBan = function(ban) - table.insert(banlist, ban) - SaveList(banlist, "banlist") + addBan = function(banId, username, bannedIdentifiers, moderator, reason, expires, expiryString, type, time) + table.insert(banlist, { + time = os.time(), + banId = banId, + username = username, + bannedIdentifiers = bannedIdentifiers, + moderator = moderator, + reason = reason, + expires = expires, + expiryString = expiryString, + type = type, + timeLeft = time, + }) + local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") + if not content then + PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end end, removeBan = function(banId) for i, ban in ipairs(banlist) do if ban.banId == banId then table.remove(banlist, i) - SaveList(banlist, "banlist") + local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") + if not content then + PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end end end return end, - addAction = function(data) - print("Adding action...") - print(json.encode(data)) - table.insert(actions, data) - SaveList(actions, "actions") + addAction = function(type, identifier, reason, moderator_name, moderator_identifier) + table.insert(actions, { + time = os.time(), + id = #actions + 1, + action = type, + discord = identifier, + reason = reason, + moderator = moderator_name, + moderatorId = moderator_identifier, + }) + local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") + if not content then + PrintDebugMessage("actions.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + return end, removeAction = function(data) for i, act in ipairs(actions) do if act.id == data.id then table.remove(actions, i) - SaveList(actions, "actions") + local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") + if not content then + PrintDebugMessage("actions.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end end end return From 0156946bb44d6a25738c8a7e49e4595944d8185f Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:21:24 +1000 Subject: [PATCH 12/26] feat(storage): refactor action history management to utilize storage functions for improved organization --- server/action_history.lua | 226 +++++++++++++++++--------------------- server/storage.lua | 49 +++------ 2 files changed, 112 insertions(+), 163 deletions(-) diff --git a/server/action_history.lua b/server/action_history.lua index 73658f89..b54b99f8 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -19,24 +19,7 @@ RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) TriggerClientEvent("EasyAdmin:ReceiveActionHistory", source, {}) return end - local history = {} - if actions then - for i, action in ipairs(actions) do - if tostring(action.discord) == tostring(discordId) then - table.insert(history, { - id = action.id, - action = action.action, - reason = action.reason, - discord = action.discord, - moderator = action.moderator, - moderatorId = action.moderatorId, - time = action.time, - }) - end - end - else - PrintDebugMessage("No actions found in the history.", 2) - end + local history = Storage.getAction(discordId) TriggerClientEvent("EasyAdmin:ReceiveActionHistory", source, history) else PrintDebugMessage("Player does not have permission to view action history.", 2) @@ -50,122 +33,111 @@ RegisterNetEvent("EasyAdmin:DeleteAction", function(actionId) PrintDebugMessage("Invalid parameters provided for action deletion.", 2) return end - for i, act in ipairs(actions) do - if act.id == actionId then - table.remove(actions, i) - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - PrintDebugMessage("Removed action: " .. json.encode(act), 4) - end - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - end - PrintDebugMessage("No matching action found for deletion.", 2) + Storage.removeAction(actionId) + PrintDebugMessage("Action deleted successfully.", 2) else PrintDebugMessage("Player does not have permission to delete actions.", 2) end end) -AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) - if GetConvar("ea_enableActionHistory", "true") == "true" then - local change = (forceChange or false) - local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") - if not content then - PrintDebugMessage("actions.json file was missing, we created a new one.", 2) - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - content = json.encode({}) - end - actions = json.decode(content) +-- MOVED TO STORAGE.LUA - if not actions then - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - PrintDebugMessage("^1Failed^7 to load Actions!\n") - PrintDebugMessage("Please check your actions file for errors, ^Action history *will not* work!^7\n") - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - return - end +-- AddEventHandler("EasyAdmin:LogAction", function(data, remove, forceChange) +-- if GetConvar("ea_enableActionHistory", "true") == "true" then +-- local change = (forceChange or false) +-- local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") +-- if not content then +-- PrintDebugMessage("actions.json file was missing, we created a new one.", 2) +-- local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode({}), -1) +-- if not saved then +-- PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) +-- end +-- content = json.encode({}) +-- end +-- actions = json.decode(content) - if data and not remove then - if data.action == "BAN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - banId = data.banId, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - expire = data.expire, - expireString = data.expireString - }) - elseif data.action == "OFFLINE BAN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - banId = data.banId, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - expire = data.expire, - expireString = data.expireString - }) - elseif data.action == "KICK" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - }) - elseif data.action == "WARN" then - table.insert(actions, { - time = os.time(), - id = #actions + 1, - action = data.action, - discord = data.discord, - reason = data.reason, - moderator = data.moderator, - moderatorId = data.moderatorId, - }) - elseif data.action == "UNBAN" then - for i, act in ipairs(actions) do - if act.banId == data.banId then - act["action"] = data.action - break - end - end - end - PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) - change=true - elseif not data then - return - end - if data and remove then - PrintDebugMessage("Removed the following data from actions:\n"..table_to_string(data), 4) - change = true - end - if change then - PrintDebugMessage("Actions changed, saving..", 4) - local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - end - PrintDebugMessage("Completed Actions Updated.", 4) - end -end) +-- if not actions then +-- PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") +-- PrintDebugMessage("^1Failed^7 to load Actions!\n") +-- PrintDebugMessage("Please check your actions file for errors, ^Action history *will not* work!^7\n") +-- PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") +-- return +-- end + +-- if data and not remove then +-- if data.action == "BAN" then +-- table.insert(actions, { +-- time = os.time(), +-- id = #actions + 1, +-- banId = data.banId, +-- action = data.action, +-- discord = data.discord, +-- reason = data.reason, +-- moderator = data.moderator, +-- moderatorId = data.moderatorId, +-- expire = data.expire, +-- expireString = data.expireString +-- }) +-- elseif data.action == "OFFLINE BAN" then +-- table.insert(actions, { +-- time = os.time(), +-- id = #actions + 1, +-- banId = data.banId, +-- action = data.action, +-- discord = data.discord, +-- reason = data.reason, +-- moderator = data.moderator, +-- moderatorId = data.moderatorId, +-- expire = data.expire, +-- expireString = data.expireString +-- }) +-- elseif data.action == "KICK" then +-- table.insert(actions, { +-- time = os.time(), +-- id = #actions + 1, +-- action = data.action, +-- discord = data.discord, +-- reason = data.reason, +-- moderator = data.moderator, +-- moderatorId = data.moderatorId, +-- }) +-- elseif data.action == "WARN" then +-- table.insert(actions, { +-- time = os.time(), +-- id = #actions + 1, +-- action = data.action, +-- discord = data.discord, +-- reason = data.reason, +-- moderator = data.moderator, +-- moderatorId = data.moderatorId, +-- }) +-- elseif data.action == "UNBAN" then +-- for i, act in ipairs(actions) do +-- if act.banId == data.banId then +-- act["action"] = data.action +-- break +-- end +-- end +-- end +-- PrintDebugMessage("Added the following to actions:\n"..table_to_string(data), 4) +-- change=true +-- elseif not data then +-- return +-- end +-- if data and remove then +-- PrintDebugMessage("Removed the following data from actions:\n"..table_to_string(data), 4) +-- change = true +-- end +-- if change then +-- PrintDebugMessage("Actions changed, saving..", 4) +-- local saved = SaveResourceFile(GetCurrentResourceName(), "actions.json", json.encode(actions, {indent = true}), -1) +-- if not saved then +-- PrintDebugMessage("^1Saving actions.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) +-- end +-- end +-- PrintDebugMessage("Completed Actions Updated.", 4) +-- end +-- end) for i, action in ipairs(actions) do if action.time + (GetConvar("ea_actionHistoryExpiry", 30) * 24 * 60 * 60) < os.time() then diff --git a/server/storage.lua b/server/storage.lua index 587e6c47..0dbc3c89 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -1,18 +1,6 @@ local banlist = {} local actions = {} - -local function SaveList(list, fileName) - local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") - if not content then - PrintDebugMessage(fileName .. ".json file was missing, we created a new one.", 2) - content = json.encode({}) - end - - local saved = SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode(list, {indent = True}), -1) - if not saved then - PrintDebugMessage("^1Saving " .. fileName .. ".json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end -end +local notes = {} local function LoadList(fileName) local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") @@ -26,29 +14,9 @@ end banlist = LoadList("banlist") actions = LoadList("actions") +notes = LoadList("notes") Storage = { - -- get = function(list, type, input) - -- for i, item in ipairs(list) do - -- if item[type] == input then - -- return item - -- end - -- end - -- return nil - -- end, - -- add = function(list, input, fileName) - -- table.insert(list, input) - -- SaveList(list, fileName) - -- end, - -- remove = function(list, type, input, fileName) - -- for i, item in ipairs(list) do - -- if item[type] == input then - -- table.remove(list, i) - -- SaveList(list, fileName) - -- end - -- end - -- return - -- end, getBan = function(banId) for i, ban in ipairs(banlist) do if ban.banId == banId then @@ -118,9 +86,9 @@ Storage = { end return end, - removeAction = function(data) + removeAction = function(actionId) for i, act in ipairs(actions) do - if act.id == data.id then + if act.id == actionId then table.remove(actions, i) local content = LoadResourceFile(GetCurrentResourceName(), "actions.json") if not content then @@ -135,5 +103,14 @@ Storage = { end return end, + getAction = function(discordId) + local actions = {} + for i, act in ipairs(actions) do + if act.discord == discordId then + table.insert(actions, act) + end + end + return actions + end, apiVersion = 1, } \ No newline at end of file From 8b0d48153f63da46c912d93cc7e01d2f23fa6119 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:04:40 +1000 Subject: [PATCH 13/26] feat(storage): add action logging functionality and permissions for action history management --- client/gui_c.lua | 1 - fxmanifest.lua | 3 ++- server/action_history.lua | 8 ++++++++ server/banlist.lua | 10 +--------- server/storage.lua | 30 +++++++++++++++++++++--------- shared/util_shared.lua | 1 + 6 files changed, 33 insertions(+), 20 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index 0abc2a4c..3dd3e848 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -891,7 +891,6 @@ function GenerateMenu() -- this is a big ass function end ExecutePluginsFunction("playerMenu", thePlayer.id) - if GetResourceState("es_extended") == "started" and not ESX then local thisItem = NativeUI.CreateItem("~y~[ESX]~s~ Options","You can buy the ESX Plugin from https://blumlaut.tebex.io to use this Feature.") diff --git a/fxmanifest.lua b/fxmanifest.lua index fde280d0..a586ec36 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -90,6 +90,7 @@ convar_category 'EasyAdmin' { { "Enable Allowlist", "$ea_enableAllowlist", "CV_BOOL", "false" }, { "Routing Bucket Options", "$ea_routingBucketOptions", "CV_BOOL", "false" }, { "Enable Action History", "$ea_enableActionHistory", "CV_BOOL", "true" }, - { "Action History Expiry", "$ea_actionHistoryExpiry", "CV_INT", "30"}, -- Recommended time is 30 days + { "Action History Expiry", "$ea_actionHistoryExpiry", "CV_INT", "30" }, -- Recommended time is 30 days + { "Default Storage Backend", "$ea_defaultStorageBackend", "CV_BOOL", "true" }, } } \ No newline at end of file diff --git a/server/action_history.lua b/server/action_history.lua index b54b99f8..b33afb0c 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -27,6 +27,14 @@ RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) end end) +RegisterNetEvent("EasyAdmin:LogAction", function(action) + if DoesPlayerHavePermission(source, "player.actionhistory.add") then + if not action then + PrintDebugMessage("Action not defined.", 2) + end + end +end) + RegisterNetEvent("EasyAdmin:DeleteAction", function(actionId) if DoesPlayerHavePermission(source, "player.actionhistory.delete") then if not actionId then diff --git a/server/banlist.lua b/server/banlist.lua index bc4ef267..f8c40144 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -28,7 +28,7 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } + -- local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) @@ -72,7 +72,6 @@ AddEventHandler('banCheater', function(playerId,reason) Citizen.Trace("^1EasyAdmin^7: the banCheater event is ^1deprecated^7 and has been removed! Please adjust your ^3"..GetInvokingResource().."^7 Resource to use EasyAdmin:addBan instead.") end) - function addBanExport(playerId,reason,expires,banner) local bannedIdentifiers = {} local bannedUsername = "Unknown" @@ -92,7 +91,6 @@ function addBanExport(playerId,reason,expires,banner) PrintDebugMessage("Couldn't find any Infos about Player "..playerId..", no ban issued.", 1) return false end - if expires and expires < os.time() then expires = os.time()+expires @@ -153,7 +151,6 @@ RegisterCommand("unban", function(source, args, rawCommand) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminunbannedplayer"), getName(source, false, true), args[1], "Unbanned via Command"), "ban", 16711680) end end, false) - RegisterServerEvent("EasyAdmin:editBan", function(ban) if DoesPlayerHavePermission(source, "player.ban.edit") then @@ -178,7 +175,6 @@ function unbanPlayer(banId) end exports('unbanPlayer', unbanPlayer) - function fetchBan(banId) for i,ban in ipairs(blacklist) do if ban.banid == banId then @@ -210,7 +206,6 @@ function GetFreshBanId() end exports('GetFreshBanId', GetFreshBanId) - RegisterCommand("convertbanlist", function(source, args, rawCommand) if GetConvar("ea_custombanlist", "false") == "true" then local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") @@ -265,15 +260,12 @@ function updateBan(id,newData) end end - function addBan(data) if data then table.insert(blacklist, data) end end - - function updateBlacklist(data,remove, forceChange) local change = (forceChange or false) --mark if file was changed to save up on disk writes. if GetConvar("ea_custombanlist", "false") == "true" then diff --git a/server/storage.lua b/server/storage.lua index 0dbc3c89..f6b60a21 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -1,3 +1,15 @@ +------------------------------------ +------------------------------------ +---- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING +---- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING +---- +---- +---- If you are a developer and want to change something, consider writing a plugin instead: +---- https://easyadmin.readthedocs.io/en/latest/plugins/ +---- +------------------------------------ +------------------------------------ + local banlist = {} local actions = {} local notes = {} @@ -65,6 +77,15 @@ Storage = { end return end, + getAction = function(discordId) + local actions = {} + for i, act in ipairs(actions) do + if act.discord == discordId then + table.insert(actions, act) + end + end + return actions + end, addAction = function(type, identifier, reason, moderator_name, moderator_identifier) table.insert(actions, { time = os.time(), @@ -103,14 +124,5 @@ Storage = { end return end, - getAction = function(discordId) - local actions = {} - for i, act in ipairs(actions) do - if act.discord == discordId then - table.insert(actions, act) - end - end - return actions - end, apiVersion = 1, } \ No newline at end of file diff --git a/shared/util_shared.lua b/shared/util_shared.lua index c63d3756..53dc05cc 100644 --- a/shared/util_shared.lua +++ b/shared/util_shared.lua @@ -15,6 +15,7 @@ permissions = { ["player.mute"] = false, ["player.warn"] = false, ["player.actionhistory.view"] = false, + ["player.actionhistory.add"] = false, ["player.actionhistory.delete"] = false, ["player.teleport.everyone"] = false, ["player.reports.view"] = false, From cd5ff0a53c8dcc94eff53fcd946203ea4f701090 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:46:12 +1000 Subject: [PATCH 14/26] fix(warnPlayer): correct permission check logic for warning players --- server/admin_server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin_server.lua b/server/admin_server.lua index 3c9df9e0..faa02f1b 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -764,7 +764,7 @@ Citizen.CreateThread(function() RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason) local src = source - if DoesPlayerHavePermission(src,"player.warn") and CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then + if DoesPlayerHavePermission(src,"player.warn") and not CachedPlayers[id].immune and CheckAdminCooldown(source, "warn") then SetAdminCooldown(source, "warn") reason = formatShortcuts(reason) local maxWarnings = GetConvarInt("ea_maxWarnings", 3) From 74645cd14b2a90c816eba72b633acbc8f27aac69 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:59:25 +1000 Subject: [PATCH 15/26] feat(action-logging): implement action logging for moderation events --- server/action_history.lua | 9 +++++++++ server/admin_server.lua | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/action_history.lua b/server/action_history.lua index b33afb0c..40a6b9fb 100644 --- a/server/action_history.lua +++ b/server/action_history.lua @@ -12,6 +12,11 @@ local actions = {} +moderationNotification = GetConvar("ea_moderationNotification", "false") +reportNotification = GetConvar("ea_reportNotification", "false") +detailNotification = GetConvar("ea_detailNotification", "false") +minimumMatchingIdentifierCount = GetConvarInt("ea_minIdentifierMatches", 2) + RegisterNetEvent("EasyAdmin:GetActionHistory", function(discordId) if DoesPlayerHavePermission(source, "player.actionhistory.view") then if not discordId then @@ -32,6 +37,8 @@ RegisterNetEvent("EasyAdmin:LogAction", function(action) if not action then PrintDebugMessage("Action not defined.", 2) end + Storage.addAction(action.type, action.discordId, action.reason, action.moderator, action.moderatorId, action.expire, action.expireString) + PrintDebugMessage("Action logged successfully.", 2) end end) @@ -43,6 +50,8 @@ RegisterNetEvent("EasyAdmin:DeleteAction", function(actionId) end Storage.removeAction(actionId) PrintDebugMessage("Action deleted successfully.", 2) + local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification + SendWebhookMessage(preferredWebhook, string.format(GetLocalisedText("actionhistorydeleted"), getName(source, false, true), actionId), "", 16777214) else PrintDebugMessage("Player does not have permission to delete actions.", 2) end diff --git a/server/admin_server.lua b/server/admin_server.lua index faa02f1b..2edccc75 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -780,8 +780,7 @@ Citizen.CreateThread(function() SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) local test_name = getName(source, true, false) -- local warn = { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = test_name, moderatorId = CachedPlayers[source].discord } - Storage.addAction("WARN", CachedPlayers[id].discord, reason, test_name, CachedPlayers[source].discord) - -- TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) From df983c8d78e55cb6aa34c705f113d534eeac3d1c Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:20:18 +1000 Subject: [PATCH 16/26] feat(storage): enhance storage functionality with new ban management methods and action logging --- server/admin_server.lua | 3 +- server/banlist.lua | 161 ++++++++++++++++++++++------------------ server/storage.lua | 54 +++++++++++++- 3 files changed, 144 insertions(+), 74 deletions(-) diff --git a/server/admin_server.lua b/server/admin_server.lua index 2edccc75..faa02f1b 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -780,7 +780,8 @@ Citizen.CreateThread(function() SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) local test_name = getName(source, true, false) -- local warn = { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = test_name, moderatorId = CachedPlayers[source].discord } - TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) + Storage.addAction("WARN", CachedPlayers[id].discord, reason, test_name, CachedPlayers[source].discord) + -- TriggerEvent("EasyAdmin:LogAction", { action = "WARN", discord = CachedPlayers[id].discord, reason = reason, moderator = getName(source, true, false), moderatorId = CachedPlayers[source].discord }) if WarnedPlayers[id].warns >= maxWarnings then if GetConvar("ea_warnAction", "kick") == "kick" then SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) diff --git a/server/banlist.lua b/server/banlist.lua index f8c40144..7ccb1e77 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -30,6 +30,7 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) -- local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) + Storage.addAction("BAN", CachedPlayers[playerId].discordId, reason, getName(source), source, expires, formatDateString(expires)) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) @@ -58,6 +59,7 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) --local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "OFFLINE BAN", os.time()) + Storage.addAction("OFFLINE BAN", CachedPlayers[playerId].discordId, reason, getName(source), source, expires, formatDateString(expires)) -- updateBlacklist( ban ) TriggerEvent("EasyAdmin:LogAction", ban) PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) @@ -101,7 +103,7 @@ function addBanExport(playerId,reason,expires,banner) -- local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = banner or "Unknown", reason = reason, expire = expires, expireString = formatDateString(expires) } -- updateBlacklist( ban ) Storage.addBan(GetFreshBanId(), bannedUsername, bannedIdentifiers, banner or "Unknown", reason, expires, formatDateString(expires), "BAN", os.time()) - + Storage.addAction("BAN", bannedIdentifiers[1], reason, banner or "Unknown", source, expires, formatDateString(expires)) if source then PrintDebugMessage("Player "..getName(source,true).." added ban "..reason, 3) end @@ -116,6 +118,7 @@ end exports('addBan', addBanExport) AddEventHandler("EasyAdmin:addBan", addBanExport) +-- Is this required anymore with storage updates? RegisterServerEvent("EasyAdmin:updateBanlist", function(playerId) local src = source if DoesPlayerHavePermission(source, "player.ban.view") then @@ -129,7 +132,7 @@ end) RegisterServerEvent("EasyAdmin:requestBanlist", function() local src = source if DoesPlayerHavePermission(source, "player.ban.view") then - TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, blacklist) + TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, Storage.getBanlist()) PrintDebugMessage("Banlist Requested by "..getName(src,true), 3) end end) @@ -154,34 +157,37 @@ end, false) RegisterServerEvent("EasyAdmin:editBan", function(ban) if DoesPlayerHavePermission(source, "player.ban.edit") then - updateBan(ban.banid,ban) + Storage.updateBan(ban.banid, ban) + --updateBan(ban.banid,ban) -- TODO Webhook end end) function unbanPlayer(banId) - local thisBan = nil - for i,ban in ipairs(blacklist) do - if ban.banid == banId then - thisBan = ban - break - end - end - if thisBan == nil then - return false - end - UnbanId(banId) - return true + return Storage.removeBan(banId) + -- local thisBan = nil + -- for i,ban in ipairs(blacklist) do + -- if ban.banid == banId then + -- thisBan = ban + -- break + -- end + -- end + -- if thisBan == nil then + -- return false + -- end + -- UnbanId(banId) + -- return true end exports('unbanPlayer', unbanPlayer) function fetchBan(banId) - for i,ban in ipairs(blacklist) do - if ban.banid == banId then - return ban - end - end - return false + return Storage.getBan(banId) + -- for i,ban in ipairs(blacklist) do + -- if ban.banid == banId then + -- return ban + -- end + -- end + -- return false end exports('fetchBan', fetchBan) @@ -206,6 +212,7 @@ function GetFreshBanId() end exports('GetFreshBanId', GetFreshBanId) +-- Is this required with removal of custom ban list convar? RegisterCommand("convertbanlist", function(source, args, rawCommand) if GetConvar("ea_custombanlist", "false") == "true" then local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") @@ -266,6 +273,7 @@ function addBan(data) end end +-- Good to remove? function updateBlacklist(data,remove, forceChange) local change = (forceChange or false) --mark if file was changed to save up on disk writes. if GetConvar("ea_custombanlist", "false") == "true" then @@ -369,76 +377,83 @@ function updateBlacklist(data,remove, forceChange) end function BanIdentifier(identifier,reason) - updateBlacklist( {identifiers = {identifier} , banner = "Unknown", reason = reason, expire = 10444633200} ) + Storage.addBan(GetFreshBanId(), "Unknown", {identifier}, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time())` + --updateBlacklist( {identifiers = {identifier} , banner = "Unknown", reason = reason, expire = 10444633200} ) end +-- Unclear what the purpose of this is... converted anyway function BanIdentifiers(identifier,reason) - updateBlacklist( {identifiers = identifier , banner = "Unknown", reason = reason, expire = 10444633200} ) + Storage.addBan(GetFreshBanId(), "Unknown", identifier, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time())` + --updateBlacklist( {identifiers = identifier , banner = "Unknown", reason = reason, expire = 10444633200} ) end function UnbanIdentifier(identifier) - if identifier then - for i,ban in pairs(blacklist) do - for index,id in pairs(ban.identifiers) do - if identifier == id then - table.remove(blacklist,i) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end + Storage.removeBanIdentifier(identifier) + -- if identifier then + -- for i,ban in pairs(blacklist) do + -- for index,id in pairs(ban.identifiers) do + -- if identifier == id then + -- table.remove(blacklist,i) + -- local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) + -- if not saved then + -- PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + -- end - if GetConvar("ea_custombanlist", "false") == "true" then - TriggerEvent("ea_data:removeBan", ban) - end - PrintDebugMessage("removed ban as per unbanidentifier func", 4) - return - end - end - end - end + -- if GetConvar("ea_custombanlist", "false") == "true" then + -- TriggerEvent("ea_data:removeBan", ban) + -- end + -- PrintDebugMessage("removed ban as per unbanidentifier func", 4) + -- return + -- end + -- end + -- end + -- end end function UnbanId(id) - for i,ban in pairs(blacklist) do - if ban.banid == id then - table.remove(blacklist,i) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end + Storage.removeBan(id) + -- for i,ban in pairs(blacklist) do + -- if ban.banid == id then + -- table.remove(blacklist,i) + -- local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) + -- if not saved then + -- PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + -- end - if GetConvar("ea_custombanlist", "false") == "true" then - TriggerEvent("ea_data:removeBan", ban) - end - TriggerEvent("EasyAdmin:LogAction", {action = "UNBAN", banId = id }) - end - end + -- if GetConvar("ea_custombanlist", "false") == "true" then + -- TriggerEvent("ea_data:removeBan", ban) + -- end + -- TriggerEvent("EasyAdmin:LogAction", {action = "UNBAN", banId = id }) + -- end + -- end end + +-- Yet to test this in-game, I believe there might be an issue passing json tables to this pluggable storage function performBanlistUpgrades() local upgraded = false - + local banlist = Storage.getBanList() local takenIds = {} - for i,b in pairs(blacklist) do + for i,b in pairs(banlist) do if takenIds[b.banid] then local freshId = GetFreshBanId() PrintDebugMessage("ID "..b.banid.." was assigned twice, reassigned to "..freshId, 4) - blacklist[i].banid = freshId + banlist[i].banid = freshId upgraded = true end takenIds[b.banid] = true end takenIds=nil - for i,ban in pairs(blacklist) do + for i,ban in pairs(banlist) do if type(i) == "string" then PrintDebugMessage("Ban "..ban.banid.." had a string as indice, fixed it.", 4) - blacklist[i] = nil - table.insert(blacklist,ban) + banlist[i] = nil + table.insert(banlist,ban) upgraded = true end end - for i,ban in ipairs(blacklist) do + for i,ban in ipairs(banlist) do if ban.identifiers then for k, identifier in pairs(ban.identifiers) do if identifier == "" then @@ -453,9 +468,9 @@ function performBanlistUpgrades() ban.expireString = formatDateString(ban.expire) end end - if blacklist[1] and (blacklist[1].identifier or blacklist[1].steam or blacklist[1].discord) then + if banlist[1] and (banlist[1].identifier or banlist[1].steam or banlist[1].discord) then Citizen.Trace("Upgrading Banlist...\n", 4) - for i,ban in ipairs(blacklist) do + for i,ban in ipairs(banlist) do if not ban.identifiers then ban.identifiers = {} PrintDebugMessage("Ban "..ban.banid.." had no identifiers, added one.", 4) @@ -482,20 +497,22 @@ function performBanlistUpgrades() end Citizen.Trace("Banlist Upgraded.\n", 4) end + Storage.updateBanlist(banlist) return upgraded end function IsIdentifierBanned(theIdentifier) - local identifierfound = false - for index,value in ipairs(blacklist) do - for i,identifier in ipairs(value.identifiers) do - if theIdentifier == identifier then - identifierfound = true - end - end - end - return identifierfound + return Storage.getBanIdentifier(theIdentifier) + -- local identifierfound = false + -- for index,value in ipairs(blacklist) do + -- for i,identifier in ipairs(value.identifiers) do + -- if theIdentifier == identifier then + -- identifierfound = true + -- end + -- end + -- end + -- return identifierfound end exports('IsIdentifierBanned', IsIdentifierBanned) \ No newline at end of file diff --git a/server/storage.lua b/server/storage.lua index f6b60a21..9463eada 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -35,7 +35,19 @@ Storage = { return ban end end - return nil + return false + end, + getBanIdentifier = function(identifiers) + local found = false + for i, ban in ipairs(banlist) do + for j, identifier in ipairs(identifiers) do + if ban.bannedIdentifiers[identifier] then + found = true + break + end + end + end + return found end, addBan = function(banId, username, bannedIdentifiers, moderator, reason, expires, expiryString, type, time) table.insert(banlist, { @@ -60,6 +72,19 @@ Storage = { PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) end end, + updateBan = function(banId, .....) + end, + updateBanlist = function(banlist) + local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") + if not content then + PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + end + end, removeBan = function(banId) for i, ban in ipairs(banlist) do if ban.banId == banId then @@ -72,11 +97,38 @@ Storage = { local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) if not saved then PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + return false + end + return true + else + return false + end + end + end, + removeBanIdentifier = function(identifiers) do + for i, ban in ipairs(banlist) do + for j, identifier in ipairs(identifiers) do + if ban.bannedIdentifiers[identifier] then + table.remove(banlist, i) + local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") + if not content then + PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) + content = json.encode({}) + end + local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) + if not saved then + PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) + return + end + return end end end return end, + getBanlist = function() + return banlist + end, getAction = function(discordId) local actions = {} for i, act in ipairs(actions) do From 7c1af0b0ff4eb58d683eebe2decf1709f65776bc Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Tue, 13 May 2025 00:21:43 +1000 Subject: [PATCH 17/26] fix(banlist, storage): remove unnecessary backticks and clarify updateBan function --- server/banlist.lua | 4 ++-- server/storage.lua | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/banlist.lua b/server/banlist.lua index 7ccb1e77..b051cdc3 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -377,13 +377,13 @@ function updateBlacklist(data,remove, forceChange) end function BanIdentifier(identifier,reason) - Storage.addBan(GetFreshBanId(), "Unknown", {identifier}, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time())` + Storage.addBan(GetFreshBanId(), "Unknown", {identifier}, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time()) --updateBlacklist( {identifiers = {identifier} , banner = "Unknown", reason = reason, expire = 10444633200} ) end -- Unclear what the purpose of this is... converted anyway function BanIdentifiers(identifier,reason) - Storage.addBan(GetFreshBanId(), "Unknown", identifier, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time())` + Storage.addBan(GetFreshBanId(), "Unknown", identifier, "Unknown", reason, 10444633200, formatDateString(10444633200), "BAN", os.time()) --updateBlacklist( {identifiers = identifier , banner = "Unknown", reason = reason, expire = 10444633200} ) end diff --git a/server/storage.lua b/server/storage.lua index 9463eada..405d3967 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -72,8 +72,9 @@ Storage = { PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) end end, - updateBan = function(banId, .....) - end, + -- Not too sure what this one does + -- updateBan = function(banId, .....) + -- end, updateBanlist = function(banlist) local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") if not content then @@ -105,7 +106,7 @@ Storage = { end end end, - removeBanIdentifier = function(identifiers) do + removeBanIdentifier = function(identifiers) for i, ban in ipairs(banlist) do for j, identifier in ipairs(identifiers) do if ban.bannedIdentifiers[identifier] then From d670b3d5bcd5a0e884e9b0b849790ab3b4113234 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Fri, 6 Jun 2025 23:48:18 +1000 Subject: [PATCH 18/26] refactor(storage): remove unused notes variable and fix getBanlist function name --- server/storage.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/storage.lua b/server/storage.lua index 405d3967..77c13418 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -12,7 +12,6 @@ local banlist = {} local actions = {} -local notes = {} local function LoadList(fileName) local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") @@ -26,7 +25,6 @@ end banlist = LoadList("banlist") actions = LoadList("actions") -notes = LoadList("notes") Storage = { getBan = function(banId) @@ -127,7 +125,7 @@ Storage = { end return end, - getBanlist = function() + getBanList = function() return banlist end, getAction = function(discordId) From ac60157e229eef470077696f8a9a5d2d5f6a3e31 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Fri, 6 Jun 2025 23:58:27 +1000 Subject: [PATCH 19/26] fix(gui): correct localization string for action history submenu refactor(fxmanifest): remove unused default storage backend option --- client/gui_c.lua | 2 +- fxmanifest.lua | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index 4e4cd203..ff211a54 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -843,7 +843,7 @@ function GenerateMenu() -- this is a big ass function actionHistoryMenu:AddItem(noActionsItem) end for i, action in ipairs(actionHistory) do - local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, "Reason: " .. action.reason or "", true) + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, GetLocalisedText("reason")": " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) if action.action == "BAN" and permissions["player.ban.remove"] then local actionUnban = NativeUI.CreateItem(GetLocalisedText("unbanplayer"), GetLocalisedText("unbanplayerguide")) diff --git a/fxmanifest.lua b/fxmanifest.lua index fb780073..6e3de5a9 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -90,6 +90,5 @@ convar_category 'EasyAdmin' { { "Routing Bucket Options", "$ea_routingBucketOptions", "CV_BOOL", "false" }, { "Enable Action History", "$ea_enableActionHistory", "CV_BOOL", "true" }, { "Action History Expiry", "$ea_actionHistoryExpiry", "CV_INT", "30" }, -- Recommended time is 30 days - { "Default Storage Backend", "$ea_defaultStorageBackend", "CV_BOOL", "true" }, } } \ No newline at end of file From 9e6ba5fc787637deba25460e0a7fbca995f168b0 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 8 Jun 2025 14:14:05 +1000 Subject: [PATCH 20/26] fix(gui): correct string concatenation in action history submenu fix(banlist): update function name for consistency in banlist retrieval refactor(storage): rename local variable for clarity in action retrieval --- client/gui_c.lua | 2 +- server/banlist.lua | 2 +- server/storage.lua | 11 +++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index ff211a54..fe510525 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -843,7 +843,7 @@ function GenerateMenu() -- this is a big ass function actionHistoryMenu:AddItem(noActionsItem) end for i, action in ipairs(actionHistory) do - local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, GetLocalisedText("reason")": " .. action.reason or "", true) + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, GetLocalisedText("reason") .. ": " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) if action.action == "BAN" and permissions["player.ban.remove"] then local actionUnban = NativeUI.CreateItem(GetLocalisedText("unbanplayer"), GetLocalisedText("unbanplayerguide")) diff --git a/server/banlist.lua b/server/banlist.lua index 6297200f..8aad4cf1 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -149,7 +149,7 @@ end) RegisterServerEvent("EasyAdmin:requestBanlist", function() local src = source if DoesPlayerHavePermission(source, "player.ban.view") then - TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, Storage.getBanlist()) + TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, Storage.getBanList()) PrintDebugMessage("Banlist Requested by "..getName(src,true), 3) end end) diff --git a/server/storage.lua b/server/storage.lua index 77c13418..351cae72 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -129,13 +129,16 @@ Storage = { return banlist end, getAction = function(discordId) - local actions = {} - for i, act in ipairs(actions) do + local userActions = {} + for _, act in ipairs(actions) do + print(act) if act.discord == discordId then - table.insert(actions, act) + print(discordId, type(discordId)) + print(act.discord, type(act.discord)) + table.insert(userActions, act) end end - return actions + return userActions end, addAction = function(type, identifier, reason, moderator_name, moderator_identifier) table.insert(actions, { From f33334db8206c65df21727100d2e82a2f1ccc031 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 8 Jun 2025 14:15:48 +1000 Subject: [PATCH 21/26] fix(gui): correct string concatenation in action history submenu fix(server): update getBanlist function name for consistency refactor(server): rename local variable for clarity in getAction function --- client/gui_c.lua | 2 +- server/banlist.lua | 2 +- server/storage.lua | 11 +++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/gui_c.lua b/client/gui_c.lua index ff211a54..fe510525 100644 --- a/client/gui_c.lua +++ b/client/gui_c.lua @@ -843,7 +843,7 @@ function GenerateMenu() -- this is a big ass function actionHistoryMenu:AddItem(noActionsItem) end for i, action in ipairs(actionHistory) do - local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, GetLocalisedText("reason")": " .. action.reason or "", true) + local actionSubmenu = _menuPool:AddSubMenu(actionHistoryMenu, "[#"..action.id.."] " .. action.action .. " by " .. action.moderator, GetLocalisedText("reason") .. ": " .. action.reason or "", true) actionSubmenu:SetMenuWidthOffset(menuWidth) if action.action == "BAN" and permissions["player.ban.remove"] then local actionUnban = NativeUI.CreateItem(GetLocalisedText("unbanplayer"), GetLocalisedText("unbanplayerguide")) diff --git a/server/banlist.lua b/server/banlist.lua index 6297200f..8aad4cf1 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -149,7 +149,7 @@ end) RegisterServerEvent("EasyAdmin:requestBanlist", function() local src = source if DoesPlayerHavePermission(source, "player.ban.view") then - TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, Storage.getBanlist()) + TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, Storage.getBanList()) PrintDebugMessage("Banlist Requested by "..getName(src,true), 3) end end) diff --git a/server/storage.lua b/server/storage.lua index 77c13418..351cae72 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -129,13 +129,16 @@ Storage = { return banlist end, getAction = function(discordId) - local actions = {} - for i, act in ipairs(actions) do + local userActions = {} + for _, act in ipairs(actions) do + print(act) if act.discord == discordId then - table.insert(actions, act) + print(discordId, type(discordId)) + print(act.discord, type(act.discord)) + table.insert(userActions, act) end end - return actions + return userActions end, addAction = function(type, identifier, reason, moderator_name, moderator_identifier) table.insert(actions, { From 2e1a357a3cb5fc5e21ef9a114a70af0d4caee1c6 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:48:05 +1000 Subject: [PATCH 22/26] fix(kick): streamline action logging by using Storage for kick actions --- server/admin_server.lua | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/admin_server.lua b/server/admin_server.lua index bbf5c542..2f558cff 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -325,12 +325,7 @@ Citizen.CreateThread(function() SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(source, false, true), getName(playerId, true, true), reason), "kick", 16711680) PrintDebugMessage("Kicking Player "..getName(source, true).." for "..reason, 3) if GetConvar("ea_enableActionHistory", "true") == "true" then - local playerDiscord = GetPlayerIdentifierByType(playerId, 'discord') - local discordId - if playerDiscord then - discordId = playerDiscord:match("discord:(%d+)") - TriggerEvent("EasyAdmin:LogAction", { action = "kick", license = discordId, reason = reason, banner = getName(source, true, true)}) - end + Storage.addAction("KICK", CachedPlayers[playerId].discord, reason, getName(source), CachedPlayers[source].discord) end DropPlayer(playerId, string.format(GetLocalisedText("kicked"), getName(source), reason) ) elseif CachedPlayers[playerId].immune then From f4e7c15feb4d3d08a866dccf07186181e5714933 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:46:04 +1000 Subject: [PATCH 23/26] fix(ban): improve ban action logging and standardize identifier usage --- server/admin_server.lua | 8 +++++++- server/banlist.lua | 35 +++++++---------------------------- server/storage.lua | 23 ++++++++++------------- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/server/admin_server.lua b/server/admin_server.lua index 2f558cff..4e88b368 100644 --- a/server/admin_server.lua +++ b/server/admin_server.lua @@ -325,7 +325,12 @@ Citizen.CreateThread(function() SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(source, false, true), getName(playerId, true, true), reason), "kick", 16711680) PrintDebugMessage("Kicking Player "..getName(source, true).." for "..reason, 3) if GetConvar("ea_enableActionHistory", "true") == "true" then - Storage.addAction("KICK", CachedPlayers[playerId].discord, reason, getName(source), CachedPlayers[source].discord) + local playerDiscord = GetPlayerIdentifierByType(playerId, 'discord') + local discordId + if playerDiscord then + discordId = playerDiscord:match("discord:(%d+)") + TriggerEvent("EasyAdmin:LogAction", { action = "kick", license = discordId, reason = reason, banner = getName(source, true, true)}) + end end DropPlayer(playerId, string.format(GetLocalisedText("kicked"), getName(source), reason) ) elseif CachedPlayers[playerId].immune then @@ -972,6 +977,7 @@ Citizen.CreateThread(function() local matchingIdentifierCount = 0 local matchingIdentifiers = {} local showProgress = GetConvar("ea_presentDeferral", "true") + local blacklist = Storage.getBanList() deferrals.defer() Wait(0) diff --git a/server/banlist.lua b/server/banlist.lua index 8aad4cf1..4afc1b1e 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -33,12 +33,13 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) end reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) + local banId = GetFreshBanId() -- local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } - Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) - Storage.addAction("BAN", CachedPlayers[playerId].discordId, reason, getName(source), source, expires, formatDateString(expires)) + Storage.addBan(banId, username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) + Storage.addAction("BAN", CachedPlayers[playerId].discordId, reason, getName(source), CachedPlayers[source].discordId) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) + SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(banId) ), "ban", 16711680) DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) elseif CachedPlayers[playerId].immune then TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) @@ -69,9 +70,9 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) --local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "OFFLINE BAN", os.time()) - Storage.addAction("OFFLINE BAN", CachedPlayers[playerId].discordId, reason, getName(source), source, expires, formatDateString(expires)) + Storage.addAction("OFFLINE BAN", CachedPlayers[playerId].discordId, reason, getName(source), CachedPlayers[source].discordId) -- updateBlacklist( ban ) - TriggerEvent("EasyAdmin:LogAction", ban) + -- TriggerEvent("EasyAdmin:LogAction", ban) PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminofflinebannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ) ), "ban", 16711680) end @@ -230,6 +231,7 @@ end) ---Generates a new unique ban ID ---@return number @The next available ban ID function GetFreshBanId() + local blacklist = Storage.getBanList() if blacklist[#blacklist] then return blacklist[#blacklist].banid+1 else @@ -315,29 +317,6 @@ end ---@return nil function updateBlacklist(data,remove, forceChange) local change = (forceChange or false) --mark if file was changed to save up on disk writes. - if GetConvar("ea_custombanlist", "false") == "true" then - PrintDebugMessage("You are using a Custom Banlist System, this is ^3not currently supported^7 and WILL cause issues! Only use this if you know what you are doing, otherwise, disable ea_custombanlist.", 1) - if data and not remove then - addBan(data) - TriggerEvent("ea_data:addBan", data) - - elseif data and remove then - UnbanId(data.banid) - elseif not data then - TriggerEvent('ea_data:retrieveBanlist', function(banlist) - blacklist = banlist - PrintDebugMessage("updated banlist custom banlist", 4) - for i,theBan in ipairs(blacklist) do - if theBan.expire < os.time() then - table.remove(blacklist,i) - PrintDebugMessage("removing old ban custom banlist", 4) - TriggerEvent("ea_data:removeBan", theBan) - end - end - end) - end - return - end local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") if not content then diff --git a/server/storage.lua b/server/storage.lua index 351cae72..891cfa09 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -10,8 +10,8 @@ ------------------------------------ ------------------------------------ -local banlist = {} -local actions = {} +banlist = {} +actions = {} local function LoadList(fileName) local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") @@ -29,7 +29,7 @@ actions = LoadList("actions") Storage = { getBan = function(banId) for i, ban in ipairs(banlist) do - if ban.banId == banId then + if ban.banid == banId then return ban end end @@ -39,7 +39,7 @@ Storage = { local found = false for i, ban in ipairs(banlist) do for j, identifier in ipairs(identifiers) do - if ban.bannedIdentifiers[identifier] then + if ban.identifiers[identifier] then found = true break end @@ -50,12 +50,12 @@ Storage = { addBan = function(banId, username, bannedIdentifiers, moderator, reason, expires, expiryString, type, time) table.insert(banlist, { time = os.time(), - banId = banId, + banid = banId, username = username, - bannedIdentifiers = bannedIdentifiers, - moderator = moderator, + identifiers = bannedIdentifiers, + banner = moderator, reason = reason, - expires = expires, + expire = expires, expiryString = expiryString, type = type, timeLeft = time, @@ -86,7 +86,7 @@ Storage = { end, removeBan = function(banId) for i, ban in ipairs(banlist) do - if ban.banId == banId then + if ban.banid == banId then table.remove(banlist, i) local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") if not content then @@ -107,7 +107,7 @@ Storage = { removeBanIdentifier = function(identifiers) for i, ban in ipairs(banlist) do for j, identifier in ipairs(identifiers) do - if ban.bannedIdentifiers[identifier] then + if ban.identifiers[identifier] then table.remove(banlist, i) local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") if not content then @@ -131,10 +131,7 @@ Storage = { getAction = function(discordId) local userActions = {} for _, act in ipairs(actions) do - print(act) if act.discord == discordId then - print(discordId, type(discordId)) - print(act.discord, type(act.discord)) table.insert(userActions, act) end end From ca31e9242a2d63a9586d329fc68815bfb65e364b Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Mon, 9 Jun 2025 23:22:58 +1000 Subject: [PATCH 24/26] fix(ban): update action logging to use discord identifiers for ban actions --- server/banlist.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/banlist.lua b/server/banlist.lua index 4afc1b1e..cee7b64a 100644 --- a/server/banlist.lua +++ b/server/banlist.lua @@ -36,7 +36,7 @@ RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) local banId = GetFreshBanId() -- local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires), action = "BAN", time = os.time() } Storage.addBan(banId, username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "BAN", os.time()) - Storage.addAction("BAN", CachedPlayers[playerId].discordId, reason, getName(source), CachedPlayers[source].discordId) + Storage.addAction("BAN", CachedPlayers[playerId].discord, reason, getName(source), CachedPlayers[source].discord) -- updateBlacklist( ban ) PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(banId) ), "ban", 16711680) @@ -70,7 +70,7 @@ RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expir reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) --local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires, action = "OFFLINE BAN", time = os.time() } Storage.addBan(GetFreshBanId(), username, bannedIdentifiers, getName(source), reason, expires, formatDateString(expires), "OFFLINE BAN", os.time()) - Storage.addAction("OFFLINE BAN", CachedPlayers[playerId].discordId, reason, getName(source), CachedPlayers[source].discordId) + Storage.addAction("OFFLINE BAN", CachedPlayers[playerId].discord, reason, getName(source), CachedPlayers[source].discord) -- updateBlacklist( ban ) -- TriggerEvent("EasyAdmin:LogAction", ban) PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) From 21cf0257a6c3beb196af9e784f3bcfa8ac77791f Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:18:32 +1000 Subject: [PATCH 25/26] (update): Added preliminary version control --- server/storage.lua | 57 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/server/storage.lua b/server/storage.lua index 891cfa09..207d43b4 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -10,21 +10,43 @@ ------------------------------------ ------------------------------------ +local currentVersion = 1 banlist = {} actions = {} local function LoadList(fileName) local content = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json") + local defaultData = { + version = currentVersion, + data = {} + } if content then - return json.decode(content) + local decoded = json.decode(content) + + if not decoded then + decoded = defaultData + + elseif not decoded.version then + if decoded[1] or next(decoded) ~= nil then + decoded = { + version = currentVersion, + data = decoded + } + else + decoded = defaultData + end + end + + SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode(decoded, { indent=true }), -1) + return decoded else - SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode({}), -1) - return {} + SaveResourceFile(GetCurrentResourceName(), fileName .. ".json", json.encode(defaultData, { indent=true }), -1) + return defaultData end end -banlist = LoadList("banlist") -actions = LoadList("actions") +banlist = LoadList("banlist").data +actions = LoadList("actions").data Storage = { getBan = function(banId) @@ -63,7 +85,10 @@ Storage = { local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") if not content then PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) - content = json.encode({}) + content = json.encode({ + version = 1, + bans = {} + }) end local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(banlist, {indent = true}), -1) if not saved then @@ -176,4 +201,22 @@ Storage = { return end, apiVersion = 1, -} \ No newline at end of file +} + +Citizen.CreateThread(function() + local banContent = LoadResourceFile(GetCurrentResourceName(), "banlist.json") + if banContent then + local data = json.decode(banContent) + if data.version ~= currentVersion then + -- Update logic + end + end + + local actionContent = LoadResourceFile(GetCurrentResourceName(), "actions.json") + if actionContent then + local data = json.decode(actionContent) + if data.version ~= currentVersion then + -- Update logic + end + end +end) \ No newline at end of file From 148bf6b688283c732841ca960ee996be6fd99e00 Mon Sep 17 00:00:00 2001 From: DukeOfCheese <81943364+DukeOfCheese@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:20:33 +1000 Subject: [PATCH 26/26] (update): Remove comments --- server/storage.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/storage.lua b/server/storage.lua index 207d43b4..8469a5d6 100644 --- a/server/storage.lua +++ b/server/storage.lua @@ -45,6 +45,10 @@ local function LoadList(fileName) end end +local function updateList(fileName) + +end + banlist = LoadList("banlist").data actions = LoadList("actions").data @@ -208,7 +212,7 @@ Citizen.CreateThread(function() if banContent then local data = json.decode(banContent) if data.version ~= currentVersion then - -- Update logic + updateList('banlist') end end @@ -216,7 +220,7 @@ Citizen.CreateThread(function() if actionContent then local data = json.decode(actionContent) if data.version ~= currentVersion then - -- Update logic + updateList('actions') end end end) \ No newline at end of file