Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions [core]/es_extended/server/classes/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -433,27 +433,34 @@ function CreateExtendedPlayer(playerId, identifier, ssn, group, accounts, invent
reason = reason or "Unknown"
if not tonumber(money) then
error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money))
return
return false
end
if money <= 0 then
error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money))
return false
end
local account = self.getAccount(accountName)
if not account then
error(("Tried To Set Add To Invalid Account ^5%s^1 For Player ^5%s^1!"):format(accountName, self.playerId))
return false
end
money = account.round and ESX.Math.Round(money) or money
if self.accounts[account.index].money - money > self.accounts[account.index].money then
error(("Tried To Underflow Account ^5%s^1 For Player ^5%s^1!"):format(accountName, self.playerId))
return false
end
if money > 0 then
local account = self.getAccount(accountName)

if account then
money = account.round and ESX.Math.Round(money) or money
if self.accounts[account.index].money - money > self.accounts[account.index].money then
error(("Tried To Underflow Account ^5%s^1 For Player ^5%s^1!"):format(accountName, self.playerId))
return
end
self.accounts[account.index].money = self.accounts[account.index].money - money

self.triggerEvent("esx:setAccountMoney", account)
TriggerEvent("esx:removeAccountMoney", self.source, accountName, money, reason)
else
error(("Tried To Set Add To Invalid Account ^5%s^1 For Player ^5%s^1!"):format(accountName, self.playerId))
if Config.CustomInventory == "ox" and Inventory and Inventory.accounts and Inventory.accounts[accountName] then
local removed = Inventory.RemoveItem(self.source, accountName, money)
if not removed then
return false, 0
end
else
error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money))
end

self.accounts[account.index].money = self.accounts[account.index].money - money
self.triggerEvent("esx:setAccountMoney", account)
TriggerEvent("esx:removeAccountMoney", self.source, accountName, money, reason)
return true, money
end

function self.getInventoryItem(itemName)
Expand Down
11 changes: 11 additions & 0 deletions [core]/esx_whitelist/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Node modules
web/node_modules/

# Logs
*.log

# Config de entorno
.env
web/dist
web/.next/
whitelist_config.json
74 changes: 74 additions & 0 deletions [core]/esx_whitelist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<h1 align='center'>ESX Whitelist System</h1>
<p align='center'><a href='https://discord.esx-framework.org/'>Discord</a> - <a href='https://esx-framework.org/'>Website</a> - <a href='https://docs.esx-framework.org/en'>Documentation</a></p>

<p align='center'><b>Dynamic whitelist with automated rules and Discord integration</b></p>

<hr>

## ✨ Features

- 🎯 Dynamic whitelist control without restarts
- ⏱️ Grace period system before kick
- 🎨 Modern tablet-style UI panel
- 🔍 Auto-detect identifiers (License, Steam, Discord, XBL, FiveM)
- 🤖 Automated rules (player count, admin presence, scheduled)
- 🔔 Discord webhook logs + role verification
- ⚡ Optimized performance (6x faster queries)
- 🔐 Secure with rate limiting & validation

---

## 📦 Installation

1. Extract to `resources/esx_whitelist`
2. Add to `server.cfg`: `ensure esx_whitelist`
3. Configure Discord token in `server/cfg_discord.lua` (optional)
4. Restart server

**Requirements:** ESX Legacy, oxmysql, ox_lib

---

## 🎮 Commands

**In-Game (Admin)**
```
/whitelist - Open panel
/wl_add [id] - Add player
/wl_check [id] - Check status
```

**Console**
```
wl_add [identifier] - Add by identifier
wl_remove [identifier] - Remove player
wl_on / wl_off - Toggle whitelist
```

---

## ⚙️ Configuration

Edit `config.lua`:
```lua
Config.Locale = 'en'
Config.UICommand = 'whitelist'
Config.AdminGroups = { 'admin', 'mod' }
```

Discord token in `server/cfg_discord.lua`:
```lua
Config.DiscordBotToken = "YOUR_BOT_TOKEN"
```

---

## 💬 Support

- [ESX Discord](https://discord.esx-framework.org/)
- [ESX Documentation](https://docs.esx-framework.org/en)

---

<p align='center'><b>Developed by ESX TEAM - Alx</b></p>
<p align='center'>Made with ❤️ for the ESX Community</p>
143 changes: 143 additions & 0 deletions [core]/esx_whitelist/client/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
local isWhitelistEnabled = false
local isGracePeriodActive = false
local gracePeriodEndTime = 0
local isUIVisible = false
local translations = {}

local function loadLocaleFile()
local localeFile = LoadResourceFile(GetCurrentResourceName(), 'locales/' .. Config.Locale .. '.json')
if localeFile then
translations = json.decode(localeFile)
end
end

local function T(key, ...)
local str = translations[key] or key
if not str then return key end
if ... then
local success, result = pcall(string.format, str, ...)
return success and result or str
end
return str
end

RegisterNetEvent('esx_whitelist:stateChanged', function(enabled)
isWhitelistEnabled = enabled
end)

RegisterNetEvent('esx_whitelist:startGracePeriod', function(seconds)
isGracePeriodActive = true
gracePeriodEndTime = GetGameTimer() + (seconds * 1000)

CreateThread(function()
while isGracePeriodActive and GetGameTimer() < gracePeriodEndTime do
Wait(1000)
local remainingSeconds = math.ceil((gracePeriodEndTime - GetGameTimer()) / 1000)
if remainingSeconds > 0 then
ESX.ShowNotification(string.format('~r~%s~s~\n%s', T('whitelist_active'), T('remaining_time', remainingSeconds)))
end
end
isGracePeriodActive = false
end)
end)

RegisterNetEvent('esx_whitelist:cancelGracePeriod', function()
isGracePeriodActive = false
gracePeriodEndTime = 0
ESX.ShowNotification('~g~' .. T('grace_cancelled'))
end)

local function toggleUI(visible)
isUIVisible = visible
SetNuiFocus(visible, visible)

if not visible then
SendNUIMessage({
action = 'closeUI'
})
end
end

RegisterCommand(Config.UICommand, function()
if isUIVisible then return end

ESX.TriggerServerCallback('esx_whitelist:getConfig', function(serverConfig)
if not serverConfig then
ESX.ShowNotification('~r~' .. T('no_permission'))
return
end

toggleUI(true)
SendNUIMessage({
action = 'openUI',
data = serverConfig
})
end)
end)

RegisterNUICallback('closeUI', function(data, cb)
toggleUI(false)
cb('ok')
end)

RegisterNUICallback('updateConfig', function(configData, cb)
ESX.TriggerServerCallback('esx_whitelist:updateConfig', function(success)
cb(success)
end, configData)
end)

RegisterNUICallback('testWebhook', function(data, cb)
ESX.TriggerServerCallback('esx_whitelist:testWebhook', function(success)
if success then
ESX.ShowNotification('~g~' .. T('webhook_sent'))
end
cb(success)
end)
end)

RegisterNUICallback('getWhitelistEntries', function(data, cb)
ESX.TriggerServerCallback('esx_whitelist:getWhitelistEntries', function(entries)
cb(entries or {})
end)
end)

RegisterNUICallback('getConfig', function(data, cb)
ESX.TriggerServerCallback('esx_whitelist:getConfig', function(serverConfig)
cb(serverConfig or {})
end)
end)

RegisterNUICallback('managePlayer', function(data, cb)
ESX.TriggerServerCallback('esx_whitelist:managePlayer', function(success, message)
cb({success = success, message = message})
end, data)
end)

RegisterNUICallback('toggleWhitelistStatus', function(data, cb)
ESX.TriggerServerCallback('esx_whitelist:toggleWhitelistStatus', function(success)
cb(success)
end, data)
end)

local function DisableControls()
while isUIVisible do
Wait(0)
DisableControlAction(0, 1, true)
DisableControlAction(0, 2, true)
DisableControlAction(0, 142, true)
DisableControlAction(0, 18, true)
DisableControlAction(0, 322, true)
DisableControlAction(0, 106, true)
end
end

CreateThread(function()
loadLocaleFile()

while true do
Wait(100)
if isUIVisible then
DisableControls()
end
end
end)
14 changes: 14 additions & 0 deletions [core]/esx_whitelist/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Config = {}

Config.Locale = 'en'
Config.Debug = false

Config.UICommand = 'whitelist'

Config.AdminGroups = {
'admin',
'mod'
}

Config.ConsoleCommands = true
Config.InGameCommands = true
35 changes: 35 additions & 0 deletions [core]/esx_whitelist/fxmanifest.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
fx_version 'cerulean'

game 'gta5'

description 'ESX Dynamic Whitelist System'

version '1.0.0'

lua54 'yes'

shared_scripts {
'@es_extended/imports.lua',
'@ox_lib/init.lua',
'config.lua'
}

server_scripts {
'@oxmysql/lib/MySQL.lua',
'@es_extended/locale.lua',
'server/main.lua',
'server/cfg_discord.lua',
'server/commands.lua'
}

client_scripts {
'@es_extended/locale.lua',
'client/main.lua'
}

ui_page 'web/dist/index.html'

files {
'web/dist/**/*',
'locales/*.json'
}
25 changes: 25 additions & 0 deletions [core]/esx_whitelist/install.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- ESX Whitelist System Database Tables
-- Note: These tables are created automatically by the script
-- This file is provided for manual setup or debugging purposes only

CREATE TABLE IF NOT EXISTS `whitelist` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`player_name` VARCHAR(255) COLLATE utf8mb4_unicode_ci,
`whitelisted` TINYINT(1) NOT NULL DEFAULT 0,
`added_by` VARCHAR(255) COLLATE utf8mb4_unicode_ci,
`added_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `whitelist_identifiers` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`whitelist_id` INT NOT NULL,
`type` ENUM('steam', 'license', 'discord', 'xbl', 'live', 'fivem') NOT NULL,
`identifier` VARCHAR(255) NOT NULL COLLATE utf8mb4_bin,
FOREIGN KEY (`whitelist_id`) REFERENCES `whitelist`(`id`) ON DELETE CASCADE,
UNIQUE KEY `unique_identifier` (`type`, `identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Indexes for better performance
CREATE INDEX `idx_whitelisted` ON `whitelist`(`whitelisted`);
CREATE INDEX `idx_whitelist_id` ON `whitelist_identifiers`(`whitelist_id`);
CREATE INDEX `idx_identifier` ON `whitelist_identifiers`(`identifier`);
Loading
Loading