------------------------------------
------------------------------------
---- 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/
----
------------------------------------
------------------------------------
-- Cooldowns for Admin Actions
AdminCooldowns = {}
---@param src number|string @The player source
---@param action string @The name of the admin action
---@return boolean @True if allowed to perform action, false if cooldown is active
function CheckAdminCooldown(src, action)
local numSrc = tonumber(src)
if not numSrc then return true end
if AdminCooldowns[numSrc] then
if AdminCooldowns[numSrc][action] then
TriggerClientEvent("EasyAdmin:showNotification", src, GetLocalisedText("waitbeforeusingagain"))
return false
end
end
return true
end
-- Sets a cooldown for a specific admin action
---@param src number|string
---@param action string
function SetAdminCooldown(src, action)
local numSrc = tonumber(src)
local coolTime = GetConvarInt("ea_adminCooldown:"..tostring(action), 0)
if action and numSrc and coolTime > 0 then
action = tostring(action)
AdminCooldowns[src] = AdminCooldowns[src] or {}
AdminCooldowns[src][action] = true
Citizen.SetTimeout(1000*coolTime, function()
if AdminCooldowns[src] then
AdminCooldowns[src][action] = nil
end
end)
end
end
-- Chat Reminder Code
function sendRandomReminder()
reminderTime = GetConvarInt("ea_chatReminderTime", 0)
if reminderTime ~= 0 and #ChatReminders > 0 then
local reminder = ChatReminders[ math.random( #ChatReminders ) ] -- select random reminder from table
local adminNames = ""
local t = {}
for i,_ in pairs(OnlineAdmins) do
table.insert(t, getName(i))
end
for i,n in ipairs(t) do
if i == 1 then
adminNames = n
elseif i == #t then
adminNames = adminNames.." "..n
else
adminNames = adminNames.." "..n..","
end
end
t=nil
if adminNames == "" then adminNames = "@admins" end -- if no admins are online just print @admins
reminder = string.gsub(reminder, "@admins", adminNames)
reminder = string.gsub(reminder, "@bancount", #blacklist)
reminder = string.gsub(reminder, "@time", os.date("%X", os.time()))
reminder = string.gsub(reminder, "@date", os.date("%x", os.time()))
TriggerClientEvent("chat:addMessage", -1, { args = { "EasyAdmin", reminder } })
end
end
-- Sends a global announcement
---@param text string
---@return boolean
function announce(reason)
if reason then
TriggerClientEvent("EasyAdmin:showNotification", -1, "[" .. GetLocalisedText("announcement") .. "] " .. reason)
return true
else
return false
end
end
exports('announce', announce)
Citizen.CreateThread(function()
--Wait(10000)
reminderTime = GetConvarInt("ea_chatReminderTime", 0)
if reminderTime ~= 0 then
while true do
Wait(reminderTime*60000)
sendRandomReminder()
end
else
while true do
Wait(20000)
sendRandomReminder() -- check for changes in the convar
end
end
end)
-- Gets all identifiers for a player
---@param src number
---@return table
function getAllPlayerIdentifiers(playerId) --Gets all info that could identify a player
local identifiers = GetPlayerIdentifiers(playerId)
local tokens = {}
if GetConvar("ea_useTokenIdentifiers", "true") == "true" then
if not GetNumPlayerTokens or not GetPlayerToken then
PrintDebugMessage("Server Version is below artifact 3335, disabling Token identifiers, please consider updating your FXServer!", 1)
SetConvar("ea_useTokenIdentifiers", "false")
PrintDebugMessage("Set ea_useTokenIdentifiers to false for this session.", 1)
return identifiers
end
for i=0,GetNumPlayerTokens(playerId) do
table.insert(tokens, GetPlayerToken(playerId, i))
end
end
return mergeTables(identifiers, tokens)
end
exports('getAllPlayerIdentifiers', getAllPlayerIdentifiers)
function checkForChangedIdentifiers(playerIds, bannedIds)
local unbannedIds = {}
for _,playerId in pairs(playerIds) do
local thisIdBanned = false
for _,bannedId in pairs(bannedIds) do
if playerId == bannedId then
thisIdBanned = true
end
end
if not thisIdBanned then --They have a new/changed identifier
table.insert(unbannedIds, playerId)
end
end
return unbannedIds
end
AddEventHandler('playerDropped', function (reason)
if OnlineAdmins[source] then
OnlineAdmins[source] = nil
end
if FrozenPlayers[source] then
FrozenPlayers[source] = nil
for i,_ in pairs(OnlineAdmins) do
TriggerLatentClientEvent("EasyAdmin:SetPlayerFrozen", i, 1000, source, nil)
end
end
if MutedPlayers[source] then
MutedPlayers[source] = nil
for i,_ in pairs(OnlineAdmins) do
TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, source, nil)
end
end
PrintDebugMessage(source.." disconnected.", 4)
end)
local Contributors = {
['736521574383091722'] = true, -- Jaccosf
['1001065851790839828'] = true, -- robbybaseplate
['840695262460641311'] = true, -- Knight
['270731163822325770'] = true, -- Skypo
['186980021850734592'] = true, -- coleminer0112
['469916940710707231'] = true, -- Grav
['882247593818726451'] = true, -- DukeOfCheese
}
RegisterServerEvent("EasyAdmin:GetInfinityPlayerList", function()
PrintDebugMessage(getName(source, true).." requested Playerlist.", 4)
if IsPlayerAdmin(source) then
local l = {}
local players = GetPlayers()
for i, player in pairs(players) do
local player = tonumber(player)
local cachedPlayer = cachePlayer(player)
local pData = { id = cachedPlayer.id, name = cachedPlayer.name, immune = cachedPlayer.immune, discord = cachedPlayer.discord, contributor = Contributors[cachedPlayer.discord], developer = cachedPlayer.discord == "178889658128793600" }
l[#l + 1] = pData
end
-- each player is more or less 2000bytes big.
TriggerLatentClientEvent("EasyAdmin:GetInfinityPlayerList", source, 200000, l)
end
end)
function GetOnlineAdmins()
return OnlineAdmins
end
exports('GetOnlineAdmins', GetOnlineAdmins)
function IsPlayerAdmin(pid)
return OnlineAdmins[pid]
end
exports('IsPlayerAdmin', IsPlayerAdmin)
Citizen.CreateThread(function()
if not CachedPlayers or GetVersion() == nil then
print("^7--------------------------------------------------------------")
print("^1EasyAdmin self-test failed! Your EasyAdmin **will not work**, likely you edited some files and broke EasyAdmin in the progress, please reinstall EasyAdmin.")
print("^7--------------------------------------------------------------")
return
end
if GetConvar("gamename", "gta5") == "rdr3" then
RedM = true
PrintDebugMessage("Starting in rdr3 Mode.", 4)
else
RedM = false
PrintDebugMessage("Starting in gta5 Mode.", 4)
end
AnonymousAdmins = {}
loadLanguageStrings()
moderationNotification = GetConvar("ea_moderationNotification", "false")
reportNotification = GetConvar("ea_reportNotification", "false")
detailNotification = GetConvar("ea_detailNotification", "false")
minimumMatchingIdentifierCount = GetConvarInt("ea_minIdentifierMatches", 2)
RegisterServerEvent('EasyAdmin:amiadmin', function()
local source = source
cachePlayer(source) -- this will do nothing if player is already cached.
if CachedPlayers[source].lastPermRequest and CachedPlayers[source].lastPermRequest+10 > os.time() then
PrintDebugMessage(getName(source).." hit Permission Check Ratelimit! "..CachedPlayers[source].lastPermRequest+10-os.time().." seconds left.", 3)
return
end
CachedPlayers[source].lastPermRequest = os.time()
local identifiers = getAllPlayerIdentifiers(source)
local perms = {}
for perm,val in pairs(permissions) do
local thisPerm = DoesPlayerHavePermission(source, perm)
if perm == "player.screenshot" and not screenshots then
thisPerm = false
end
--if (perm == "teleport" or perm == "spectate") and infinity then
--if (perm == "spectate") and infinity then
-- thisPerm = false
--end
if thisPerm == true then
OnlineAdmins[source] = true
end
perms[perm] = thisPerm
PrintDebugMessage("Processed Perm "..perm.." for "..getName(source, true)..", result: "..tostring(thisPerm), 3)
end
TriggerLatentClientEvent("EasyAdmin:adminresponse", source, 10000, perms)
TriggerClientEvent('chat:addSuggestion', source, '/easyadmin', "EasyAdmin Menu", {{name="report or player id", help="[Optional] Report or Player ID"}})
TriggerClientEvent('chat:addSuggestion', source, '/ea', "EasyAdmin Menu", {{name="report or player id", help="[Optional] Report or Player ID"}})
if GetConvar("ea_enableReportCommand", "true") == "true" then
TriggerClientEvent('chat:addSuggestion', source, '/'..GetConvar("ea_reportCommandName", "report"), "Report player", {{name='player', help="player name / id"}, {name='reason', help="Reason"}})
end
if GetConvar("ea_enableCallAdminCommand", "true") == "true" then
TriggerClientEvent('chat:addSuggestion', source, '/'..GetConvar("ea_callAdminCommandName", "calladmin"), "Call Admin", {{name='reason', help="Reason"}})
end
if RedM then
-- give player the right settings to work with
local key = GetConvar("ea_defaultKey", "none")
TriggerClientEvent("EasyAdmin:SetSetting", source, "button", key)
end
if GetConvar("ea_alwaysShowButtons", "false") == "true" then
TriggerClientEvent("EasyAdmin:SetSetting", source, "forceShowGUIButtons", true)
else
TriggerClientEvent("EasyAdmin:SetSetting", source, "forceShowGUIButtons", false)
end
if updateAvailable then
TriggerClientEvent("EasyAdmin:SetSetting", source, "updateAvailable", updateAvailable)
end
-- if you remove this code then you're a killjoy, can't we have nice things? just once? it's not like this changes the whole admin menu or how it behaves, its a single subtitle.
if os.date("%d/%m") == "22/08" then
local age = tonumber(os.date("%Y"))-2017 local ordinal = "th" last_digit = age % 10 if last_digit == 1 and age ~= 11 then ordinal = 'st' elseif last_digit == 2 and age ~= 12 then ordinal = 'nd' elseif last_digit == 3 and age ~= 13 then ordinal = 'rd' end
TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeTitle", "~b~Today is EasyAdmin's "..age..""..ordinal.." birthday! :)")
elseif os.date("%m") == "06" and (tonumber(os.date("%d")) >= 1 and tonumber(os.date("%d")) <= 14) then
TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeLogo", "pride")
elseif os.date("%m") == "04" and os.date("%d") == "01" then
TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeLogo", "logo-hardadmin")
TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeBanner", "banner-hardadmin")
end
if (infinity) then
TriggerClientEvent("EasyAdmin:SetSetting", source, "infinity", true)
end
TriggerLatentClientEvent("EasyAdmin:fillShortcuts", source, 10000, MessageShortcuts)
TriggerLatentClientEvent("EasyAdmin:SetLanguage", source, 10000, strings)
end)
RegisterServerEvent("EasyAdmin:kickPlayer", function(playerId,reason)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
if DoesPlayerHavePermission(source, "player.kick") and CheckAdminCooldown(source, "kick") and not CachedPlayers[playerId].immune then
SetAdminCooldown(source, "kick")
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)
DropPlayer(playerId, string.format(GetLocalisedText("kicked"), getName(source), reason) )
elseif CachedPlayers[playerId].immune then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune"))
end
end)
RegisterServerEvent("EasyAdmin:requestSpectate", function(playerId)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
if DoesPlayerHavePermission(source, "player.spectate") and CheckAdminCooldown(source, "spectate") then
SetAdminCooldown(source, "spectate")
PrintDebugMessage("Player "..getName(source,true).." Requested Spectate to "..getName(playerId,true), 3)
local tgtPed = GetPlayerPed(playerId)
if tgtPed == 0 then
-- ped does not exist left or not loaded in yet
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound"))
return
end
if playerId == source then
return
end
local tgtCoords = GetEntityCoords(tgtPed)
if tgtCoords.x == 0.00 and tgtCoords.y == 0.00 then
-- loaded in but still spawning
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound"))
return
end
local playerBucket = GetPlayerRoutingBucket(playerId)
local sourceBucket = GetPlayerRoutingBucket(source)
if sourceBucket ~= playerBucket then
-- upon spectate request, the admin needs to be set to the target player's bucket if not already
SetPlayerRoutingBucket(source, playerBucket)
end
local playerData = { coords = tgtCoords, selfbucket = sourceBucket }
TriggerClientEvent("EasyAdmin:requestSpectate", source, playerId, playerData)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('spectatedplayer'), getName(source, false, true), getName(playerId, true, true)), "spectate", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:requestBucket", function(playerId)
if DoesPlayerHavePermission(source, "player.spectate") then
local playerBucket = GetPlayerRoutingBucket(playerId)
local sourceBucket = GetPlayerRoutingBucket(source)
if sourceBucket ~= playerBucket then
-- mismatch in buckets, the admin needs to be set to the target player
SetPlayerRoutingBucket(source, playerBucket)
local tgtCoords = GetEntityCoords(GetPlayerPed(playerId))
local playerData = { coords = tgtCoords }
TriggerClientEvent("EasyAdmin:requestSpectate", source, playerId, playerData)
end
end
end)
RegisterServerEvent("EasyAdmin:resetBucket", function(originalBucket)
if DoesPlayerHavePermission(source, "player.spectate") then
local sourceBucket = GetPlayerRoutingBucket(source)
if sourceBucket ~= originalBucket then
-- restore the moderator's original bucket to the cached start bucket
SetPlayerRoutingBucket(source, originalBucket)
end
end
end)
RegisterServerEvent("EasyAdmin:JoinPlayerRoutingBucket", function(playerId)
if DoesPlayerHavePermission(source, "player.bucket") then
SetPlayerRoutingBucket(source, GetPlayerRoutingBucket(playerId))
end
end)
RegisterServerEvent("EasyAdmin:ForcePlayerRoutingBucket", function(playerId)
if DoesPlayerHavePermission(source, "player.bucket") then
SetPlayerRoutingBucket(playerId, GetPlayerRoutingBucket(source))
end
end)
function cleanupArea(type, radius, player)
if not radius then radius = "global" end
if (onesync ~= "off" and onesync ~= "legacy") then
local toDelete = {}
if type == "cars" then
toDelete = GetAllVehicles()
elseif type == "peds" then
toDelete = GetAllPeds()
elseif type == "props" then
toDelete = GetAllObjects()
end
PrintDebugMessage("server-known entities: "..table_to_string(toDelete), 4)
for _,entity in pairs(toDelete) do
PrintDebugMessage("starting deletion for entity "..entity, 4)
if DoesEntityExist(entity) and not (type == "cars" and IsPedAPlayer(GetPedInVehicleSeat(entity, -1))) and not (type == "peds" and IsPedAPlayer(entity)) then
if radius == "global" then
PrintDebugMessage("deleting entity "..entity, 3)
DeleteEntity(entity)
else
local entityCoords = GetEntityCoords(entity)
local playerCoords = GetEntityCoords(GetPlayerPed(player))
if #(playerCoords - entityCoords) < radius then
PrintDebugMessage("deleting entity "..entity, 3)
DeleteEntity(entity)
end
end
end
end
return true
else
return false
end
end
exports('cleanupArea', cleanupArea)
RegisterServerEvent("EasyAdmin:requestCleanup", function(type, radius, deep)
local source=source
if DoesPlayerHavePermission(source, "server.cleanup."..type) then
PrintDebugMessage("Player "..getName(source,true).." Requested Cleanup for "..type, 3)
cleanupArea(type, radius, source)
if deep then
TriggerClientEvent("EasyAdmin:requestCleanup", source, type, radius)
end
TriggerClientEvent("EasyAdmin:showNotification", source, string.format(GetLocalisedText("finishedcleaning"), GetLocalisedText(type)))
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('admincleanedup'), getName(source, false, true), type, radius), "cleanup", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:SetGameType", function(text)
if DoesPlayerHavePermission(source, "server.convars") then
PrintDebugMessage("Player "..getName(source,true).." set Gametype to "..text, 3)
SetGameType(text)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), "gametype", text), "settings", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:SetMapName", function(text)
if DoesPlayerHavePermission(source, "server.convars") then
PrintDebugMessage("Player "..getName(source,true).." set Map Name to "..text, 3)
SetMapName(text)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), "mapname", text), "settings", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:StartResource", function(text)
if DoesPlayerHavePermission(source, "server.resources.start") then
PrintDebugMessage("Player "..getName(source,true).." started Resource "..text, 3)
StartResource(text)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminstartedresource'), getName(source, false, true), text), "settings", 65280)
end
end)
RegisterServerEvent("EasyAdmin:StopResource", function(text)
if DoesPlayerHavePermission(source, "server.resources.stop") then
PrintDebugMessage("Player "..getName(source,true).." stopped Resource "..text, 3)
StopResource(text)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminstoppedresource'), getName(source, false, true), text), "settings", 16711680)
end
end)
RegisterServerEvent("EasyAdmin:SetConvar", function(convarname, convarvalue)
if DoesPlayerHavePermission(source, "server.convars") then
PrintDebugMessage("Player "..getName(source,true).." set convar "..convarname.. " to "..convarvalue, 3)
SetConvar(convarname, convarvalue)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), convarname, convarvalue), "settings", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:Announce", function(text)
if DoesPlayerHavePermission(source, "server.announce") then
PrintDebugMessage("Player "..getName(source,true).." sent a announcement: "..text, 3)
announce(text)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminannouncement'), getName(source, false, true), text), "settings", 16777214)
end
end)
RegisterServerEvent("EasyAdmin:TeleportPlayerToCoords", function(playerId,tgtCoords)
-- Validate playerId before proceeding
if playerId ~= -1 and (not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped) then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
local source=source
if DoesPlayerHavePermission(source, "player.teleport.single") and CheckAdminCooldown(source, "teleport") then
SetAdminCooldown(source, "teleport")
PrintDebugMessage("Player "..getName(source,true).." requsted teleport to "..tgtCoords.x..", "..tgtCoords.y..", "..tgtCoords.z, 3)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
local playerName = getName(playerId, true, true)
if playerId == -1 then
playerName = GetLocalisedText("allplayers")
end
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("teleportedtoplayer"), playerName, getName(source, false, true)), "teleport", 16777214)
TriggerClientEvent("EasyAdmin:TeleportRequest", playerId, false, tgtCoords)
end
end)
RegisterServerEvent("EasyAdmin:TeleportAdminToPlayer", function(id)
local source=source
if not CachedPlayers[id].dropped and DoesPlayerHavePermission(source, "player.teleport.single") and CheckAdminCooldown(source, "teleport") then
SetAdminCooldown(source, "teleport")
local tgtPed = GetPlayerPed(id)
if tgtPed == 0 then
-- ped does not exist left or not loaded in yet
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound"))
return
end
if id == source then
return
end
local tgtCoords = GetEntityCoords(tgtPed)
if tgtCoords.x == 0.00 and tgtCoords.y == 0.00 then
-- loaded in but still spawning
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound"))
return
end
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("teleportedtoplayer"), getName(source, false, true), getName(id, true, true)), "teleport", 16777214)
TriggerClientEvent('EasyAdmin:TeleportRequest', source, id,tgtCoords)
else
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound"))
PrintDebugMessage('EASYADMIN FAILED TO TELEPORT'..source..' TO ID: '..id, 2)
end
end)
RegisterServerEvent("EasyAdmin:TeleportPlayerBack", function(id)
local source=source
if not CachedPlayers[id].dropped and DoesPlayerHavePermission(source, "player.teleport.single") then
TriggerClientEvent('EasyAdmin:TeleportPlayerBack', id)
end
end)
-- Slaps a player for a given amount of HP
---@param playerId number
---@param slapAmount number
---@return boolean
function slapPlayer(playerId,slapAmount)
if not CachedPlayers[playerId].immune then
TriggerClientEvent("EasyAdmin:SlapPlayer", playerId, slapAmount)
return true
else
return false
end
end
exports('slapPlayer', slapPlayer)
RegisterServerEvent("EasyAdmin:SlapPlayer", function(playerId,slapAmount)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
if DoesPlayerHavePermission(source, "player.slap") and CheckAdminCooldown(source, "slap") and slapPlayer(playerId, slapAmount) then
SetAdminCooldown(source, "slap")
PrintDebugMessage("Player "..getName(source,true).." slapped "..getName(playerId,true).." for "..slapAmount.." HP", 3)
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminslappedplayer"), getName(source, false, true), getName(playerId, true, true), slapAmount), "slap", 16777214)
elseif CachedPlayers[playerId].immune then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune"))
end
end)
-- Freezes or unfreezes a player
---@param playerId number
---@param toggle boolean
---@return boolean
function freezePlayer(playerId, toggle)
if not toggle then toggle = not FrozenPlayers[playerId] end
if not CachedPlayers[playerId].immune then
FrozenPlayers[playerId] = (toggle == true or nil)
TriggerClientEvent("EasyAdmin:FreezePlayer", playerId, toggle)
for i,_ in pairs(OnlineAdmins) do
TriggerLatentClientEvent("EasyAdmin:SetPlayerFrozen", i, 1000, playerId, (toggle == true or nil))
end
return true
else
return false
end
end
exports('freezePlayer', freezePlayer)
RegisterServerEvent("EasyAdmin:FreezePlayer", function(playerId,toggle)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
if DoesPlayerHavePermission(source, "player.freeze") and not CachedPlayers[playerId].immune and CheckAdminCooldown(source, "freeze") then
local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification
freezePlayer(playerId, toggle)
if toggle then
SetAdminCooldown(source, "freeze")
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminfrozeplayer"), getName(source, false, true), getName(playerId, true, true)), "freeze", 16777214)
PrintDebugMessage("Player "..getName(source,true).." froze "..getName(playerId,true), 3)
else
SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminunfrozeplayer"), getName(source, false, true), getName(playerId, true, true)), "freeze", 16777214)
PrintDebugMessage("Player "..getName(source,true).." unfroze "..getName(playerId,true), 3)
end
elseif CachedPlayers[playerId].immune then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune"))
end
end)
scrinprogress = false
-- Checks if a screenshot is in progress
---@return boolean
function isScreenshotInProgress()
return scrinprogress
end
exports('isScreenshotInProgress', isScreenshotInProgress)
RegisterServerEvent("EasyAdmin:TakeScreenshot", function(playerId)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
if scrinprogress then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("screenshotinprogress"))
return
end
local src=source
local playerId = playerId
local invokingResource
if GetInvokingResource() and GetInvokingResource() ~= GetCurrentResourceName() then
invokingResource = "`"..GetInvokingResource().."`"
end
if DoesPlayerHavePermission(source, "player.screenshot") and CheckAdminCooldown(source, "screenshot") then
SetAdminCooldown(source, "screenshot")
scrinprogress = true
thistemporaryevent = RegisterServerEvent("EasyAdmin:TookScreenshot", function(result)
if result == "ERROR" then return false end
res = matchURL(tostring(result))
PrintDebugMessage("Screenshot taken, result:\n "..res, 4)
SendWebhookMessage(moderationNotification, string.format(GetLocalisedText("admintookscreenshot"), invokingResource or getName(src), getName(playerId, true, true), res), "screenshot", 16777214, "Screenshot Captured", res)
TriggerClientEvent('chat:addMessage', src, { template = '', args = { res } })
TriggerClientEvent("chat:addMessage", src, { args = { "EasyAdmin", string.format(GetLocalisedText("screenshotlink"), res) } })
PrintDebugMessage("Screenshot for Player "..getName(playerId,true).." done, "..res.." requsted by"..getName(src,true), 3)
scrinprogress = false
RemoveEventHandler(thistemporaryevent)
end)
TriggerClientEvent("EasyAdmin:CaptureScreenshot", playerId)
local timeoutwait = 0
repeat
timeoutwait=timeoutwait+1
Wait(5000)
if timeoutwait == 5 then
RemoveEventHandler(thistemporaryevent)
scrinprogress = false -- cancel screenshot, seems like it failed
PrintDebugMessage("Screenshot timed out", 4)
TriggerClientEvent("EasyAdmin:showNotification", src, "Screenshot Failed!")
end
until not scrinprogress
end
end)
RegisterServerEvent("EasyAdmin:mutePlayer", function(playerId)
-- Validate playerId before proceeding
if not playerId or not CachedPlayers[playerId] or CachedPlayers[playerId].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
local src = source
if DoesPlayerHavePermission(src,"player.mute") and not CachedPlayers[playerId].immune and CheckAdminCooldown(source, "mute") then
SetAdminCooldown(source, "mute")
local muted = mutePlayer(playerId, not MutedPlayers[playerId])
if muted then
if MutedPlayers[playerId] then
TriggerClientEvent("EasyAdmin:showNotification", src, getName(playerId) .. " " .. GetLocalisedText("playermuted"))
SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminmutedplayer"), getName(source, false, true), getName(playerId, false, true)), "mute", 16777214)
else
TriggerClientEvent("EasyAdmin:showNotification", src, getName(playerId) .. " " .. GetLocalisedText("playerunmuted"))
SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminunmutedplayer"), getName(source, false, false), getName(playerId, false, true)), "mute", 16777214)
end
else
-- todo: handle false retval
end
end
end)
-- Mutes or unmutes a player
---@param playerId number
---@param toggle boolean
---@return boolean
function mutePlayer(playerId, toggle)
if not CachedPlayers[playerId].immune then
if toggle and not MutedPlayers[playerId] then
MutedPlayers[playerId] = true
if MumbleSetPlayerMuted then -- workaround for outdated servers
MumbleSetPlayerMuted(playerId, true)
end
PrintDebugMessage("muted "..getName(playerId,true), 3)
for i,_ in pairs(OnlineAdmins) do
TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, playerId, (MutedPlayers[playerId] == true or nil))
end
return true
elseif not toggle and MutedPlayers[playerId] then
MutedPlayers[playerId] = nil
if MumbleSetPlayerMuted then -- workaround for outdated servers
MumbleSetPlayerMuted(playerId, false)
end
PrintDebugMessage("unmuted "..getName(playerId,true), 3)
for i,_ in pairs(OnlineAdmins) do
TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, playerId, (MutedPlayers[playerId] == true or nil))
end
return true
else
return false
end
else
return false
end
end
exports('mutePlayer', mutePlayer)
RegisterServerEvent("EasyAdmin:SetAnonymous", function(playerId)
if DoesPlayerHavePermission(source, "anon") then
if AnonymousAdmins[source] then
AnonymousAdmins[source] = nil
PrintDebugMessage("Player "..getName(source,true).." un-anoned themself", 3)
else
AnonymousAdmins[source] = true
PrintDebugMessage("Player "..getName(source,true).." anoned themself", 3)
end
end
end)
-- Gets the name of a player, optionally including identifiers
---@param src number|string
---@param anonymousdisabled boolean
---@param identifierenabled boolean
---@return string
function getName(src,anonymousdisabled,identifierenabled)
local identifierPref = GetConvar("ea_logIdentifier", "steam,discord,license")
if identifierPref == "false" then identifierenabled = false end;
local identifiers, identifier = {}, "~No Identifier~"
if (src == 0 or src == "") then
return "Console"
else
if AnonymousAdmins[src] and not anonymousdisabled then
return GetLocalisedText("anonymous")
elseif CachedPlayers[src] and CachedPlayers[src].name then
if not identifierenabled then
return CachedPlayers[src].name
end
if not CachedPlayers[src].discord then
return CachedPlayers[src].name
end
return (string.format("%s [ %s ]", CachedPlayers[src].name, CachedPlayers[src].discord))
elseif (GetPlayerName(src)) then
local playerName = GetPlayerName(src)
if not identifierenabled then
return playerName
end
local playerDiscord = GetPlayerIdentifierByType(src, "discord") and GetPlayerIdentifierByType(src, "discord"):gsub("discord:", "") or false
if not playerDiscord then
return playerName
end
return (string.format("%s [ %s ]", playerName, playerDiscord))
else
return "Unknown - " .. src
end
end
end
exports('getName', getName)
RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason)
-- Validate id before proceeding
if not id or not CachedPlayers[id] or CachedPlayers[id].dropped then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidplayer"))
return
end
local src = source
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)
if not WarnedPlayers[id] then
WarnedPlayers[id] = {name = getName(id, true), identifiers = getAllPlayerIdentifiers(id), warns = 0}
end
WarnedPlayers[id].warns = WarnedPlayers[id].warns+1
TriggerClientEvent('chat:addMessage', id, {
template = '
Expires: ' ..
formatDateString(blacklist[bi].expire) .. '
Banned By: ' .. banMessageStaffName ..
'
Ban Reason: ' .. banMessageReason ..
'
Ban ID: ' ..
blacklist[bi].banid ..
'
' .. banMessageFooter .. '