2026-04-14 17:41:39 +02:00

1279 lines
62 KiB
Lua

------------------------------------
------------------------------------
---- 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 = '<img src="{0}" style="max-width: 400px;" />', 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 = '<div style="padding: 0.5vw; margin: 0.5vw; background-color: rgba(253, 53, 53, 0.6); border-radius: 5px;"><i class="fas fa-user-crown"></i> {0} </div>',
args = { string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings) }, color = { 255, 255, 255 }
})
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 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
elseif GetConvar("ea_warnAction", "kick") == "ban" then
local bannedIdentifiers = CachedPlayers[id].identifiers or getAllPlayerIdentifiers(id)
local bannedUsername = CachedPlayers[id].name or getName(id, true)
local expires = os.time()+GetConvarInt("ea_warningBanTime", 604800)
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 )
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)
DropPlayer(id, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) )
WarnedPlayers[id] = nil
end
end
elseif CachedPlayers[id].immune then
TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune"))
end
end)
-- Warns a player and handles kick/ban logic if max warnings are reached
---@param src number
---@param id number
---@param reason string
---@return boolean
function warnPlayerExport(src, id, reason)
-- Validate id before proceeding
if not id or not CachedPlayers[id] or CachedPlayers[id].dropped then
return false
end
if not CachedPlayers[id].immune then
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 = '<div style="padding: 0.5vw; margin: 0.5vw; background-color: rgba(253, 53, 53, 0.6); border-radius: 5px;"><i class="fas fa-user-crown"></i> {0} </div>',
args = { string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings) }, color = { 255, 255, 255 }
})
SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), src, getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680)
TriggerClientEvent("txcl:showWarning", id, src, string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss"))
if WarnedPlayers[id].warns >= maxWarnings then
if GetConvar("ea_warnAction", "kick") == "kick" then
SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), src, getName(id, true, true), reason), "kick", 16711680)
DropPlayer(id, GetLocalisedText("warnkicked"))
WarnedPlayers[id] = nil
elseif GetConvar("ea_warnAction", "kick") == "ban" then
local expires = os.time()+GetConvarInt("ea_warningBanTime", 604800)
addBanExport(id, reason, formatDateString(expires), src)
WarnedPlayers[id] = nil
end
end
return true
else
return false
end
end
exports('warnPlayer', warnPlayerExport)
-- Gets the number of warnings a player has
---@param playerId number
---@return number
function getPlayerWarnings(playerId)
if not WarnedPlayers[playerId] then
return 0
else
return WarnedPlayers[playerId].warns
end
end
exports('getPlayerWarnings', getPlayerWarnings)
AddEventHandler("EasyAdmin:GetVersion", function(cb)
cb(GetVersion())
end)
local chatEventsSupported = false
pcall(function() -- this will prevent our script from erroring if the exports are missing, also mutes any errors.
if exports.chat.registerMessageHook and exports.chat.registerMode then
chatEventsSupported = true
end
end)
if chatEventsSupported then
exports.chat:registerMessageHook(function(source, outMessage, hookRef)
if MutedPlayers[source] then
hookRef.cancel()
TriggerClientEvent("EasyAdmin:showNotification", source, getName(source) .. ", " .. GetLocalisedText("playermute"))
end
end)
else
AddEventHandler('chatMessage', function(source, name, msg)
if MutedPlayers[source] then
CancelEvent()
TriggerClientEvent("chat:addMessage", source, { args = { "EasyAdmin", GetLocalisedText("playermute") } })
TriggerClientEvent("EasyAdmin:showNotification", source, getName(source) .. ", " .. GetLocalisedText("playermute"))
end
end)
end
if GetConvar("ea_enableChat", "true") == "true" and chatEventsSupported then
exports.chat:registerMode({
name = "admins",
displayName = "Admin Chat",
color = "#19A2E3",
seObject = "easyadmin.server.chat",
cb = function(source, message, cbs)
cbs.updateMessage({
template = "^5[ADMIN CHAT]^7" .. ' {}'
})
cbs.setSeObject("easyadmin.server.chat")
end
})
end
end)
Citizen.CreateThread(function()
while true do
PerformHttpRequest("https://api.github.com/repos/Blumlaut/EasyAdmin/releases/latest", checkVersion, "GET")
Wait(3600000)
end
end)
Citizen.CreateThread(function()
-- Makes an HTTP request and returns the result
---@param url string
---@param ... any
---@return string
function HTTPRequest(url, ...)
local err,response,headers
PerformHttpRequest(url, function(e,r,h)
err,response,headers = e,r,h
end, ...)
repeat
Wait(10)
until (response)
return response
end
exports('HTTPRequest', HTTPRequest)
end)
Citizen.CreateThread(function()
AddEventHandler('playerConnecting', function(playerName, setKickReason, deferrals)
local player = source
local numIds = getAllPlayerIdentifiers(player)
local matchingIdentifierCount = 0
local matchingIdentifiers = {}
local showProgress = GetConvar("ea_presentDeferral", "true")
deferrals.defer()
Wait(0)
local deferralText = string.format(GetLocalisedText("deferral"), 0)
if showProgress == "false" then
deferralText = deferralText:sub(1, -6)
end
deferrals.update(deferralText)
PrintDebugMessage(getName(player).."'s Identifiers:\n "..table_to_string(numIds), 3)
if not blacklist then
print("^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")
print("EasyAdmin: ^1Failed^7 to load Banlist!\n")
print("EasyAdmin: Please check this error soon, ^1Bans *will not* work!^7\n")
print("^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")
deferrals.done("\n\nEasyAdmin: A fatal error occured, please contact a Server Administrator to resolve this issue.")
return
end
Wait(0)
local lastPercentage = 0
for bi,blacklisted in ipairs(blacklist) do
if showProgress == "true" then
local percentage = math.round(bi/#blacklist*100)
if bi % 12 == 0 and percentage >= lastPercentage+4 then -- only update on every 12th ban
Wait(0)
deferrals.update(string.format(GetLocalisedText("deferral"), percentage))
lastPercentage = percentage
end
end
for i,theId in ipairs(numIds) do
for ci,identifier in ipairs(blacklisted.identifiers) do
if identifier == theId and matchingIdentifiers[theId] ~= true then
matchingIdentifierCount = matchingIdentifierCount+1
matchingIdentifiers[theId] = true -- make sure we remember the identifier for later
PrintDebugMessage("IDENTIFIER MATCH! "..identifier.." Required: "..matchingIdentifierCount.."/"..minimumMatchingIdentifierCount, 3)
local notBannedIds = checkForChangedIdentifiers(numIds, blacklisted.identifiers)
if matchingIdentifierCount >= minimumMatchingIdentifierCount then
if #notBannedIds > 0 then
local newBanData = blacklisted
newBanData.identifiers = mergeTables(blacklisted.identifiers, notBannedIds) -- add newly found identifiers to the existing ban
updateBan(blacklisted.banid,newBanData) -- send it off!
end
PrintDebugMessage("Connection of "..getName(player).." Declined, Banned for "..blacklist[bi].reason..", Ban ID: "..blacklist[bi].banid.."\n", 3)
local banMessageTitleColour = GetConvar("ea_banMessageTitleColour", "#354557")
local banMessageServerName = GetConvar("ea_banMessageServerName", GetConvar("sv_projectName", "EasyAdmin"))
local banMessageShowStaff = GetConvar("ea_banMessageShowStaff", "true")
local banMessageStaffName = blacklist[bi].banner
local banMessageFooter = GetConvar("ea_banMessageFooter", "You can appeal this by ban by visiting our discord.")
local banMessageSubHeader = GetConvar("ea_banMessageSubHeader", "You have been banned from this server.")
local banMessageWatermark = GetConvar("ea_banMessageWatermark", "data:image/png;base64,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")
local banMessageReason = blacklist[bi].reason:gsub(string.format(", .*: %s", banMessageStaffName), "")
-- gives us a raw ban reason with their nickname as we don't want the staff member displayed due to our new convar // "banned by:" field
if banMessageShowStaff == "false" then
banMessageStaffName = 'Server Staff'
end
deferrals.done(
'<div style="background-color: rgba(30, 30, 30, 0.5); padding: 20px; border: solid 2px var(--color-modal-border); border-radius: var(--border-radius-normal); margin-top: 25px; position: relative;"><h1 style="color:' .. banMessageTitleColour .. ';">' .. banMessageServerName .. '</h1><br><h2>'.. banMessageSubHeader ..'</h2><br><p style="font-size: 1.25rem; padding: 0px"><strong>Expires:</strong> ' ..
formatDateString(blacklist[bi].expire) .. '<br><strong>Banned By:</strong> ' .. banMessageStaffName ..
' <br> <strong>Ban Reason:</strong> ' .. banMessageReason ..
' <br> <strong>Ban ID:</strong> <code style="letter-spacing: 2px; background-color: #ff7f5059; padding: 2px 4px; border-radius: 6px;">' ..
blacklist[bi].banid ..
'</code><br><br>' .. banMessageFooter .. ' <span style="font-style: italic;"></span></p><img src="' .. banMessageWatermark .. '" style="position: absolute;right: 15px;bottom: 15px;opacity: 65%;"></div>')
return
end
end
end
end
end
if GetConvar("ea_enableAllowlist", "false") == "true" then
deferrals.update(GetLocalisedText("checkingallowlist"))
local allowlistAttempts = 0
local allowlisted = false
repeat
allowlisted = DoesPlayerHavePermission(player, "player.allowlist")
allowlistAttempts = allowlistAttempts+1
Wait(100)
until (allowlistAttempts >= 15 or allowlisted == true)
if DoesPlayerHavePermission(player, "player.allowlist") then
deferrals.done()
else
deferrals.done(GetLocalisedText("allowlist"))
return
end
else
deferrals.done()
end
end)
end)
curVersion, isMaster = GetVersion()
local resourceName = "EasyAdmin ("..GetCurrentResourceName()..")"
function checkVersion()
local remoteVersion,remoteURL = getLatestVersion()
if GetResourceKvpString('currentVersion') ~= curVersion then
local legacyFiles = {
'__resource.lua',
'version.json',
'admin_server.lua',
'admin_client.lua',
'gui_c.lua',
'util_shared.lua',
'yarn.lock',
'.yarn.installed',
'server/bot/notifications.js',
'package.json',
'server/bot/bot.js',
'server/bot/chat_bridge.js',
'server/bot/functions.js',
'server/bot/logging.js',
'server/bot/player_events.js',
'server/bot/reports.js',
'server/bot/roles.js',
'server/bot/server_status.js',
'server/bot/commands/add_ace.js',
'server/bot/commands/add_group.js',
'server/bot/commands/announce.js',
'server/bot/commands/ban.js',
'server/bot/commands/baninfo.js',
'server/bot/commands/cleanup.js',
'server/bot/commands/configure.js',
'server/bot/commands/freeze.js',
'server/bot/commands/kick.js',
'server/bot/commands/mute.js',
'server/bot/commands/playerinfo.js',
'server/bot/commands/playerlist.js',
'server/bot/commands/refreshperms.js',
'server/bot/commands/remove_ace.js',
'server/bot/commands/remove_group.js',
'server/bot/commands/screenshot.js',
'server/bot/commands/slap.js',
'server/bot/commands/unban.js',
'server/bot/commands/unfreeze.js',
'server/bot/commands/unmute.js',
'server/bot/commands/warn.js',
'dist/commands/configure.js'
}
for i,file in pairs(legacyFiles) do
local fileExists = LoadResourceFile(GetCurrentResourceName(), file)
if fileExists then
os.remove(GetResourcePath(GetCurrentResourceName()).."/"..file)
PrintDebugMessage("Found legacy file "..file.." in EasyAdmin Folder and attempted deletion.", 2)
end
end
PrintDebugMessage('EasyAdmin has been updated, or just been installed for the first time, please restart EasyAdmin to ensure smooth operation.', 1)
SetResourceKvpNoSync('currentVersion', curVersion)
end
if isMaster then
PrintDebugMessage("You are using an unstable version of EasyAdmin, if this was not your intention, please download the latest stable version from "..remoteURL, 1)
end
if not tonumber(curVersion) then
PrintDebugMessage("EasyAdmin's Version Number is invalid, this usually means you are using a pre-release version")
elseif curVersion ~= remoteVersion and tonumber(curVersion) < tonumber(remoteVersion) then
print("\n--------------------------------------------------------------------------")
print("\n"..resourceName.." is outdated.\nNewest Version: "..remoteVersion.."\nYour Version: "..curVersion.."\nPlease update it from "..remoteURL)
print("\n--------------------------------------------------------------------------")
updateAvailable = remoteVersion
elseif tonumber(curVersion) > tonumber(remoteVersion) then
PrintDebugMessage("Your version of "..resourceName.." seems to be higher than the current stable version.", 2)
end
if GetResourceState("screenshot-basic") == "missing" then
PrintDebugMessage("screenshot-basic is not installed, screenshots unavailable", 3)
else
StartResource("screenshot-basic")
screenshots = true
end
local onesync = GetConvar("onesync", "off")
if (onesync ~= "off" and onesync ~= "legacy") then
PrintDebugMessage("Onesync is Infinity", 3)
infinity = true
end
if GetConvar("ea_defaultKey", "none") == "none" and RedM then
PrintDebugMessage("ea_defaultKey is not defined, EasyAdmin can only be opened using the /easyadmin command, to define a key:\nhttps://easyadmin.readthedocs.io/en/latest", 1)
end
end
Citizen.CreateThread(function()
function getLatestVersion()
local latestVersion,latestURL
PerformHttpRequest("https://api.github.com/repos/Blumlaut/EasyAdmin/releases/latest", function(err,response,headers)
if err == 200 then
local data = json.decode(response)
latestVersion = data.tag_name
latestURL = data.html_url
else
latestVersion = GetVersion()
latestURL = "https://github.com/Blumlaut/EasyAdmin"
end
PrintDebugMessage("Version check returned "..err..", Local Version: "..GetVersion()..", Remote Version: "..latestVersion, 4)
end, "GET")
repeat
Wait(50)
until (latestVersion and latestURL)
return latestVersion, latestURL
end
exports('getLatestVersion', getLatestVersion)
end)
Citizen.CreateThread(function()
repeat
Wait(1000)
until updateBlacklist
while true do
updateBlacklist()
Wait(300000)
end
end)
---------------------------------- END USEFUL
if GetConvar("ea_enableSplash", "true") == "true" then
local version,master = GetVersion()
if master then version = version.." (UNSTABLE PRE-RELEASE!)" end
print("\n _______ _______ _______ __ __ _______ ______ _______ _____ __ _\n |______ |_____| |______ \\_/ |_____| | \\ | | | | | \\ |\n |______ | | ______| | | | |_____/ | | | __|__ | \\_|\n Version ^3"..version.."^7")
PrintDebugMessage("Initialised.", 4)
end
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE
MutedPlayers = {} -- DO NOT TOUCH THIS
OnlineAdmins = {} -- DO NOT TOUCH THIS
ChatReminders = {} -- DO NOT TOUCH THIS
MessageShortcuts = {} -- DO NOT TOUCH THIS
WarnedPlayers = {} -- DO NOT TOUCH THIS
reports = {} -- DO NOT TOUCH THIS
FrozenPlayers = {} -- DO NOT TOUCH THIS
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE
-- DO NOT TOUCH THESE