ExxDev
TRIAL MOD.
Katılım:
18 Tem 2026
Mesajlar:
4
Beğeniler:
2
FIVEM DISCORD LOG SİSTEMİ
QBCore için giriş-çıkış, para, ölüm, item, araç ve yönetici logları
Sıfırdan başlayanlar için adım adım anlatım
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Bu Rehberde Ne Yapacağız?
Bu rehberde QBCore tabanlı FiveM sunucumuzda gerçekleşen önemli işlemleri Discord kanallarına gönderen bir log sistemi hazırlayacağız.
Hazırlayacağımız sistem:
- Oyuncu giriş logu
- Oyuncu çıkış logu
- Citizen ID ve Rockstar License logu
- Para ekleme ve çıkarma logu
- Oyuncu ölüm ve öldürme logu
- Item verme ve alma logu
- Araç satın alma logu
- Yönetici işlem logu
- Özel script logları
- Discord embed mesajları
- Log sırası ve spam koruması
Bilgilendirme: Sadece log göndermek için gerçek bir Discord botu oluşturmak zorunda değiliz. Discord webhook sistemi bizim için bot görünümünde mesaj gönderebilir.
Slash komutları, rol verme, mesaj okuma veya Discord üzerinden FiveM sunucusunu yönetme istiyorsanız ayrıca gerçek bir Discord botu gerekir. Bu rehber yalnızca güvenli sunucu log sistemi içindir.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Discord Log Kanallarını Oluşturma
Discord sunucunuzda öncelikle yalnızca yönetim ekibinin görebileceği bir kategori oluşturun:
Kod:
📁 SUNUCU LOGLARI
Bu kategorinin içerisine aşağıdaki kanalları açabilirsiniz:
Kod:
📥・giriş-çıkış-log
💰・para-log
💀・ölüm-log
📦・item-log
🚗・araç-log
🛡️・admin-log
⚙️・genel-log
Kategori izinlerinde @everyone rolünün “Kanalı Görüntüle” yetkisini kapatın.
Yalnızca kurucu, geliştirici ve gerekli yönetim rollerine görüntüleme izni verin.
Güvenlik uyarısı: Log kanallarında Citizen ID, Rockstar License ve Discord ID gibi özel bilgiler bulunabilir. Bu kanalları normal oyunculara açmayın.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. Discord Webhook Oluşturma
Webhook oluşturmak istediğiniz log kanalına girin.
Ardından:
- Kanalın yanındaki dişli simgesine basın.
- Entegrasyonlar bölümüne girin.
- Web Kancaları / Webhooks seçeneğini açın.
- Yeni Webhook butonuna basın.
- Webhook adını “ExxDev FiveM Logs” yapın.
- Mesajların gönderileceği kanalı seçin.
- Webhook URL’sini Kopyala butonuna basın.
Örnek bir webhook adresi şu yapıya benzer:
Kod:
https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN
Her kanal için ayrı webhook oluşturabilirsiniz.
Webhook adresinizi kimseyle paylaşmayın.
Webhook adresini bilen bir kişi Discord kanalınıza istediği mesajı gönderebilir. Webhook adresiniz sızarsa eski webhooku silin ve yenisini oluşturun.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3. FiveM Resource Klasörünü Oluşturma
FiveM sunucunuzun resources klasörüne girin:
Kod:
resources/[standalone]
Burada şu isimde yeni bir klasör oluşturun:
Kod:
exxdev_discordlogs
Klasör yapısı aşağıdaki gibi olmalıdır:
Kod:
exxdev_discordlogs/
├── fxmanifest.lua
├── config.lua
└── server/
└── main.lua
Dosya uzantılarının yanlışlıkla .txt olmadığından emin olun.
Yanlış:
Kod:
fxmanifest.lua.txt
config.lua.txt
main.lua.txt
Doğru:
Kod:
fxmanifest.lua
config.lua
main.lua
Windows kullanıyorsanız Dosya Gezgini üzerinden Görünüm > Dosya adı uzantıları seçeneğini açabilirsiniz.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4. fxmanifest.lua Dosyası
fxmanifest.lua dosyasını açın ve aşağıdaki kodu içerisine yapıştırın:
Kod:
fx_version 'cerulean'
game 'gta5'
author 'ExxDev'
description 'QBCore Discord Log Sistemi'
version '1.0.0'
server_scripts {
'config.lua',
'server/main.lua'
}
dependency 'qb-core'
Dosyayı kaydedin.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5. config.lua Ayarları
config.lua dosyasını açın ve aşağıdaki kodu yapıştırın:
Kod:
Config = {}
Config.BotName = 'ExxDev FiveM Logs'
-- İsteğe bağlı bot profil resmi.
-- Boş bırakırsanız webhookun kendi profil resmi kullanılır.
Config.AvatarURL = ''
Config.FooterText = 'ExxDev FiveM Log Sistemi'
-- Discord'a log gönderme aralığı.
-- Çok düşük yapmayın, aksi durumda Discord 429 hatası verebilir.
Config.SendIntervalMilliseconds = 1200
-- Hafızada en fazla tutulacak log miktarı.
Config.MaxQueue = 250
-- Bu değerin altındaki para işlemleri Discord'a gönderilmez.
-- Bütün para hareketleri için 1 yapabilirsiniz.
Config.MinimumMoneyLog = 5000
Config.LogResourceStart = true
Config.Webhooks = {
-- Sadece tek kanal kullanacaksanız webhooku General alanına yazın.
-- Diğer alanları boş bırakırsanız bütün loglar General kanalına gider.
General = 'WEBHOOK_ADRESINI_BURAYA_YAZ',
Player = '',
Money = '',
Combat = '',
Item = '',
Vehicle = '',
Admin = ''
}
Config.Colors = {
Green = 5763719,
Red = 15548997,
Yellow = 16705372,
Blue = 3447003,
Purple = 10181046,
Orange = 15105570,
Grey = 9807270
}
Tek kanal kullanacaksanız
Sadece genel-log kanalında bir webhook oluşturun ve şu alana yapıştırın:
Kod:
General = 'https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN',
Diğer alanları boş bırakın:
Kod:
Player = '',
Money = '',
Combat = '',
Item = '',
Vehicle = '',
Admin = ''
Her log için ayrı kanal kullanacaksanız
Her kanalda ayrı webhook oluşturup ilgili alana ekleyin:
Kod:
Config.Webhooks = {
General = 'GENEL_LOG_WEBHOOK',
Player = 'GIRIS_CIKIS_WEBHOOK',
Money = 'PARA_LOG_WEBHOOK',
Combat = 'OLUM_LOG_WEBHOOK',
Item = 'ITEM_LOG_WEBHOOK',
Vehicle = 'ARAC_LOG_WEBHOOK',
Admin = 'ADMIN_LOG_WEBHOOK'
}
Webhook adreslerinin başında ve sonunda gereksiz boşluk bırakmayın.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6. server/main.lua Dosyası
server klasöründeki main.lua dosyasını açın ve aşağıdaki kodun tamamını yapıştırın:
Kod:
local QBCore = exports['qb-core']:GetCoreObject()
local logQueue = {}
local queueRunning = false
local playerCache = {}
local function SafeText(value, maximumLength)
local text = tostring(value or 'Bilinmiyor')
maximumLength = tonumber(maximumLength) or 1000
-- Discord etiket ve kod görünümü suistimalini azaltır.
text = text:gsub('@', '@')
text = text:gsub('`', 'ˋ')
if #text > maximumLength then
text = text:sub(1, maximumLength - 3) .. '...'
end
return text
end
local function GetIdentifiers(src)
local identifiers = {
license = 'Bulunamadı',
discord = 'Bulunamadı',
fivem = 'Bulunamadı',
steam = 'Bulunamadı'
}
for _, identifier in ipairs(GetPlayerIdentifiers(src)) do
if identifier:sub(1, 8) == 'license:' then
identifiers.license = identifier
elseif identifier:sub(1, 8) == 'discord:' then
identifiers.discord = identifier
elseif identifier:sub(1, 6) == 'fivem:' then
identifiers.fivem = identifier
elseif identifier:sub(1, 6) == 'steam:' then
identifiers.steam = identifier
end
end
return identifiers
end
local function FormatDiscordIdentifier(identifier)
if not identifier or identifier == 'Bulunamadı' then
return 'Bulunamadı'
end
local discordId = identifier:gsub('discord:', '')
return ('<@%s> | `%s`'):format(discordId, discordId)
end
local function GetCharacterName(playerData)
if not playerData or not playerData.charinfo then
return 'Karakter yüklenmedi'
end
local firstName = playerData.charinfo.firstname or ''
local lastName = playerData.charinfo.lastname or ''
return SafeText(firstName .. ' ' .. lastName, 100)
end
local function GetJobName(playerData)
if not playerData or not playerData.job then
return 'Bilinmiyor'
end
local jobLabel = playerData.job.label or playerData.job.name or 'Bilinmiyor'
local gradeName = 'Bilinmiyor'
if playerData.job.grade then
gradeName = playerData.job.grade.name
or tostring(playerData.job.grade.level or 'Bilinmiyor')
end
return SafeText(('%s / %s'):format(jobLabel, gradeName), 100)
end
local function GetWebhook(category)
local selectedWebhook = Config.Webhooks[category]
if selectedWebhook and selectedWebhook ~= '' then
return selectedWebhook
end
return Config.Webhooks.General
end
local function StartQueue()
if queueRunning then
return
end
queueRunning = true
CreateThread(function()
while #logQueue > 0 do
local logData = table.remove(logQueue, 1)
PerformHttpRequest(
logData.webhook,
function(statusCode, responseBody, responseHeaders, errorData)
if statusCode ~= 200 and statusCode ~= 204 then
print(
('[ExxDev Discord Logs] Gönderim hatası! HTTP: %s | Hata: %s')
:format(
tostring(statusCode),
tostring(errorData or responseBody)
)
)
if statusCode == 401 or statusCode == 404 then
print(
'[ExxDev Discord Logs] Webhook adresi geçersiz veya silinmiş olabilir.'
)
elseif statusCode == 429 then
print(
'[ExxDev Discord Logs] Discord hız sınırına ulaşıldı. Gönderim aralığını yükseltin.'
)
end
end
end,
'POST',
logData.payload,
{
['Content-Type'] = 'application/json'
}
)
Wait(
math.max(
tonumber(Config.SendIntervalMilliseconds) or 1200,
500
)
)
end
queueRunning = false
-- Yeni log tam thread biterken geldiyse tekrar başlatır.
if #logQueue > 0 then
StartQueue()
end
end)
end
local function SendLog(category, title, description, color, fields)
category = tostring(category or 'General')
local webhook = GetWebhook(category)
if not webhook or webhook == '' then
print(
('[ExxDev Discord Logs] %s kategorisi için webhook ayarlanmamış.')
:format(category)
)
return false
end
if webhook:find(
'https://discord.com/api/webhooks/',
1,
true
) ~= 1 then
print(
('[ExxDev Discord Logs] %s kategorisindeki webhook adresi geçersiz.')
:format(category)
)
return false
end
local embedFields = {}
if type(fields) == 'table' then
for index = 1, math.min(#fields, 25) do
local field = fields[index]
embedFields[#embedFields + 1] = {
name = SafeText(field.name, 250),
value = SafeText(field.value, 1000),
inline = field.inline == true
}
end
end
local embed = {
title = SafeText(title, 250),
description = SafeText(description, 4000),
color = tonumber(color) or Config.Colors.Blue,
fields = embedFields,
timestamp = os.date('!%Y-%m-%dT%H:%M:%SZ'),
footer = {
text = SafeText(Config.FooterText, 200)
}
}
local payloadData = {
username = SafeText(Config.BotName, 80),
-- Oyuncu isimleri veya log metinleri etiket içerirse
-- Discord rollerine bildirim gönderilmesini engeller.
allowed_mentions = {
parse = {}
},
embeds = {
embed
}
}
if Config.AvatarURL and Config.AvatarURL ~= '' then
payloadData.avatar_url = Config.AvatarURL
end
local maximumQueue = tonumber(Config.MaxQueue) or 250
if #logQueue >= maximumQueue then
table.remove(logQueue, 1)
print(
'[ExxDev Discord Logs] Log sırası dolduğu için en eski kayıt kaldırıldı.'
)
end
logQueue[#logQueue + 1] = {
webhook = webhook,
payload = json.encode(payloadData)
}
StartQueue()
return true
end
-- Başka server scriptleri bu exportu kullanarak log gönderebilir.
exports('SendLog', SendLog)
-- Sadece server tarafındaki scriptler TriggerEvent ile kullanabilir.
-- Bunu RegisterNetEvent olarak değiştirmeyin.
AddEventHandler(
'exxdev_discordlogs:server:send',
function(category, title, description, color, fields)
SendLog(category, title, description, color, fields)
end
)
-- QBCore karakter yükleme ve giriş logu
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
if not Player or not Player.PlayerData then
return
end
local playerData = Player.PlayerData
local src = tonumber(playerData.source)
if not src then
return
end
local identifiers = GetIdentifiers(src)
local characterName = GetCharacterName(playerData)
playerCache[src] = {
gameName = GetPlayerName(src) or 'Bilinmiyor',
characterName = characterName,
citizenId = playerData.citizenid or 'Bilinmiyor',
license = identifiers.license,
discord = identifiers.discord,
job = GetJobName(playerData)
}
SendLog(
'Player',
'🟢 Oyuncu Sunucuya Girdi',
'Bir oyuncu karakterini başarıyla yükledi.',
Config.Colors.Green,
{
{
name = 'Oyuncu',
value = GetPlayerName(src) or 'Bilinmiyor',
inline = true
},
{
name = 'Server ID',
value = src,
inline = true
},
{
name = 'Karakter',
value = characterName,
inline = true
},
{
name = 'Citizen ID',
value = playerData.citizenid or 'Bilinmiyor',
inline = true
},
{
name = 'Meslek',
value = GetJobName(playerData),
inline = true
},
{
name = 'Discord',
value = FormatDiscordIdentifier(identifiers.discord),
inline = false
},
{
name = 'Rockstar License',
value = identifiers.license,
inline = false
}
}
)
end)
-- Oyuncu çıkış logu
AddEventHandler(
'playerDropped',
function(reason, resourceName, clientDropReason)
local src = source
local cached = playerCache[src]
local identifiers = GetIdentifiers(src)
SendLog(
'Player',
'🔴 Oyuncu Sunucudan Ayrıldı',
'Bir oyuncunun sunucu bağlantısı sona erdi.',
Config.Colors.Red,
{
{
name = 'Oyuncu',
value = cached and cached.gameName
or GetPlayerName(src)
or 'Bilinmiyor',
inline = true
},
{
name = 'Server ID',
value = src,
inline = true
},
{
name = 'Karakter',
value = cached and cached.characterName
or 'Karakter bilgisi bulunamadı',
inline = true
},
{
name = 'Citizen ID',
value = cached and cached.citizenId
or 'Bilinmiyor',
inline = true
},
{
name = 'Ayrılma Nedeni',
value = reason or 'Bilinmiyor',
inline = false
},
{
name = 'Discord',
value = FormatDiscordIdentifier(
cached and cached.discord
or identifiers.discord
),
inline = false
},
{
name = 'Rockstar License',
value = cached and cached.license
or identifiers.license,
inline = false
}
}
)
playerCache[src] = nil
end
)
-- QBCore para hareketleri
AddEventHandler(
'QBCore:Server:OnMoneyChange',
function(playerSource, moneyType, amount, action, reason)
local src = tonumber(playerSource)
amount = tonumber(amount)
if not src or not amount then
return
end
if amount < (tonumber(Config.MinimumMoneyLog) or 1) then
return
end
local Player = QBCore.Functions.GetPlayer(src)
local cached = playerCache[src]
local balance = 'Bilinmiyor'
if Player
and Player.PlayerData
and Player.PlayerData.money
and Player.PlayerData.money[moneyType]
then
balance = Player.PlayerData.money[moneyType]
end
local actionNames = {
add = 'Para Eklendi',
remove = 'Para Çıkarıldı',
set = 'Para Bakiyesi Ayarlandı'
}
local actionColors = {
add = Config.Colors.Green,
remove = Config.Colors.Red,
set = Config.Colors.Yellow
}
SendLog(
'Money',
'💰 ' .. (actionNames[action] or 'Para Hareketi'),
'Bir oyuncunun para bakiyesinde değişiklik gerçekleşti.',
actionColors[action] or Config.Colors.Yellow,
{
{
name = 'Oyuncu',
value = GetPlayerName(src) or 'Bilinmiyor',
inline = true
},
{
name = 'Karakter',
value = cached and cached.characterName
or 'Bilinmiyor',
inline = true
},
{
name = 'Citizen ID',
value = Player
and Player.PlayerData.citizenid
or cached
and cached.citizenId
or 'Bilinmiyor',
inline = true
},
{
name = 'Para Türü',
value = moneyType or 'Bilinmiyor',
inline = true
},
{
name = 'İşlem',
value = actionNames[action] or action or 'Bilinmiyor',
inline = true
},
{
name = 'Miktar',
value = ('$%s'):format(amount),
inline = true
},
{
name = 'Yeni Bakiye',
value = ('$%s'):format(balance),
inline = true
},
{
name = 'İşlem Nedeni',
value = reason or 'unknown',
inline = false
}
}
)
end
)
-- Bir oyuncu başka oyuncu tarafından öldürüldüğünde
AddEventHandler(
'baseevents:onPlayerKilled',
function(killerId, deathData)
local victimId = source
killerId = tonumber(killerId)
local victimCache = playerCache[victimId]
local killerCache = killerId and playerCache[killerId] or nil
local weaponHash = 'Bilinmiyor'
if type(deathData) == 'table' and deathData.weaponhash then
weaponHash = tostring(deathData.weaponhash)
end
SendLog(
'Combat',
'💀 Oyuncu Öldürüldü',
'Bir oyuncu başka bir oyuncu tarafından öldürüldü.',
Config.Colors.Red,
{
{
name = 'Ölen Oyuncu',
value = GetPlayerName(victimId) or 'Bilinmiyor',
inline = true
},
{
name = 'Ölen Karakter',
value = victimCache and victimCache.characterName
or 'Bilinmiyor',
inline = true
},
{
name = 'Öldüren Oyuncu',
value = killerId
and GetPlayerName(killerId)
or 'Bilinmiyor',
inline = true
},
{
name = 'Öldüren Karakter',
value = killerCache and killerCache.characterName
or 'Bilinmiyor',
inline = true
},
{
name = 'Ölen ID',
value = victimId,
inline = true
},
{
name = 'Öldüren ID',
value = killerId or 'Bilinmiyor',
inline = true
},
{
name = 'Silah Hash',
value = weaponHash,
inline = false
}
}
)
end
)
-- Oyuncu çevresel sebeple veya kendi kendine öldüğünde
AddEventHandler(
'baseevents:onPlayerDied',
function(killedBy, position)
local src = source
local cached = playerCache[src]
local positionText = 'Bilinmiyor'
if type(position) == 'table' then
positionText = ('X: %s | Y: %s | Z: %s'):format(
tostring(position[1] or '?'),
tostring(position[2] or '?'),
tostring(position[3] or '?')
)
end
SendLog(
'Combat',
'☠️ Oyuncu Hayatını Kaybetti',
'Oyuncu çevresel bir sebeple veya kendi kendine öldü.',
Config.Colors.Orange,
{
{
name = 'Oyuncu',
value = GetPlayerName(src) or 'Bilinmiyor',
inline = true
},
{
name = 'Karakter',
value = cached and cached.characterName
or 'Bilinmiyor',
inline = true
},
{
name = 'Server ID',
value = src,
inline = true
},
{
name = 'Konum',
value = positionText,
inline = false
}
}
)
end
)
-- txAdmin konsolundan çalıştırılabilen test komutu
RegisterCommand('testdiscordlog', function(source)
if source ~= 0 then
return
end
SendLog(
'General',
'✅ Discord Log Testi Başarılı',
'FiveM sunucusu Discord webhookuna başarıyla erişti.',
Config.Colors.Green,
{
{
name = 'Resource',
value = GetCurrentResourceName(),
inline = true
},
{
name = 'Durum',
value = 'Aktif',
inline = true
}
}
)
print('[ExxDev Discord Logs] Test logu sıraya eklendi.')
end, false)
AddEventHandler('onResourceStart', function(resourceName)
if resourceName ~= GetCurrentResourceName() then
return
end
if not Config.LogResourceStart then
return
end
SetTimeout(2000, function()
SendLog(
'General',
'⚙️ Log Sistemi Başlatıldı',
'Discord log resource’u başarıyla çalıştırıldı.',
Config.Colors.Blue,
{
{
name = 'Resource',
value = GetCurrentResourceName(),
inline = true
},
{
name = 'Sürüm',
value = '1.0.0',
inline = true
}
}
)
end)
end)
Dosyayı kaydedin.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7. server.cfg Ayarı
server.cfg dosyanızı açın.
Aşağıdaki satırların doğru sırada bulunduğundan emin olun:
Kod:
ensure baseevents
ensure qb-core
ensure exxdev_discordlogs
Log sistemi qb-core’dan sonra başlamalıdır.
baseevents resource’u ölüm ve öldürme logları için gereklidir. Standart FXServer kurulumlarında genellikle resources/[system]/baseevents klasöründe bulunur.
Sunucuyu yeniden başlatabilir veya txAdmin konsolunda şu komutları kullanabilirsiniz:
Kod:
refresh
ensure exxdev_discordlogs
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8. Sistemi Test Etme
txAdmin konsolunu açın ve şu komutu çalıştırın:
Kod:
testdiscordlog
Her şey doğruysa Discord kanalınıza şu başlıkla bir mesaj gelecektir:
Kod:
✅ Discord Log Testi Başarılı
Ardından sunucuya girip aşağıdaki işlemleri test edin:
- Karakterinizi yükleyin.
- Sunucudan çıkış yapın.
- Yüksek miktarda para ekleyip çıkarın.
- Başka bir oyuncuyla ölüm sistemini test edin.
- txAdmin konsolunda hata bulunup bulunmadığını kontrol edin.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9. Item Logu Nasıl Eklenir?
Item logu, itemi veren veya kaldıran scriptin server.lua dosyasına eklenmelidir.
Örnek bir item verme işlemi:
Kod:
local added = exports['qb-inventory']:AddItem(
src,
'water_bottle',
5,
false,
false,
'meslek-odulu'
)
İşlem başarılı olduktan sonra şu kodu ekleyin:
Kod:
if added then
exports['exxdev_discordlogs']:SendLog(
'Item',
'📦 Oyuncuya Item Verildi',
'Bir oyuncunun envanterine item eklendi.',
5763719,
{
{
name = 'Oyuncu',
value = GetPlayerName(src) or 'Bilinmiyor',
inline = true
},
{
name = 'Server ID',
value = src,
inline = true
},
{
name = 'Item',
value = 'water_bottle',
inline = true
},
{
name = 'Miktar',
value = 5,
inline = true
},
{
name = 'İşlem Nedeni',
value = 'Meslek ödülü',
inline = false
}
}
)
end
Önemli: Logu item ekleme işleminden önce göndermeyin. İşlem başarısız olsa bile Discord’a başarılı gibi yansıyabilir.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10. Araç Satın Alma Logu
Araç veritabanına başarıyla kaydedildikten sonra araç satış scriptinin server dosyasına şu yapıyı ekleyebilirsiniz:
Kod:
exports['exxdev_discordlogs']:SendLog(
'Vehicle',
'🚗 Araç Satın Alındı',
'Bir oyuncu galeriden araç satın aldı.',
3447003,
{
{
name = 'Oyuncu',
value = GetPlayerName(src) or 'Bilinmiyor',
inline = true
},
{
name = 'Server ID',
value = src,
inline = true
},
{
name = 'Araç',
value = vehicleModel,
inline = true
},
{
name = 'Plaka',
value = plate,
inline = true
},
{
name = 'Fiyat',
value = ('$%s'):format(vehiclePrice),
inline = true
}
}
)
vehicleModel, plate ve vehiclePrice değişkenlerini kullandığınız galeri scriptine göre düzenlemeniz gerekir.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
11. Yönetici İşlem Logu
Bir yönetici komutu başarıyla uygulandıktan sonra aşağıdaki yapıyı kullanabilirsiniz:
Kod:
exports['exxdev_discordlogs']:SendLog(
'Admin',
'🛡️ Yönetici İşlemi',
'Bir yönetici oyuncu üzerinde işlem uyguladı.',
10181046,
{
{
name = 'Yetkili',
value = GetPlayerName(source) or 'Konsol',
inline = true
},
{
name = 'Yetkili ID',
value = source,
inline = true
},
{
name = 'Hedef',
value = GetPlayerName(targetId) or 'Bilinmiyor',
inline = true
},
{
name = 'Hedef ID',
value = targetId,
inline = true
},
{
name = 'İşlem',
value = 'Para verildi',
inline = false
},
{
name = 'Miktar',
value = amount,
inline = true
}
}
)
Log kodunu yetki kontrolünden ve başarılı işlemden sonra çalıştırın.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12. TriggerEvent ile Log Gönderme
Export kullanmak istemiyorsanız başka bir server dosyasından şu şekilde log gönderebilirsiniz:
Kod:
TriggerEvent(
'exxdev_discordlogs:server:send',
'General',
'Özel İşlem Logu',
'Sunucuda özel bir işlem gerçekleşti.',
3447003,
{
{
name = 'Açıklama',
value = 'Buraya işlem bilgisi yazılabilir.',
inline = false
}
}
)
Güvenlik uyarısı:
Aşağıdaki kullanımı yapmayın:
Kod:
RegisterNetEvent('exxdev_discordlogs:server:send')
Log eventi client tarafından erişilebilir hâle getirilirse hile kullanan kişiler Discord kanallarınıza sahte log veya log spam gönderebilir.
Log gönderimi yalnızca server dosyalarında yapılmalıdır.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13. Sık Karşılaşılan Hatalar
Discord’a hiçbir log gelmiyor
- Webhook adresini yeniden kopyalayın.
- Webhook adresinin başında veya sonunda boşluk olmadığını kontrol edin.
- config.lua dosyasını kaydettiğinizden emin olun.
- Resource’u yeniden başlatın.
- txAdmin konsolunda testdiscordlog komutunu kullanın.
- Sunucunun Discord’a HTTPS bağlantısı kurabildiğini kontrol edin.
HTTP 401 veya 404 hatası
Webhook silinmiş, değiştirilmiş veya yanlış kopyalanmış olabilir. Discord’dan yeni bir webhook oluşturun.
HTTP 429 hatası
Kısa sürede çok fazla log gönderilmiştir.
config.lua içerisindeki değeri yükseltin:
Kod:
Config.SendIntervalMilliseconds = 2000
Ayrıca para, item ve giriş logları için ayrı webhooklar kullanabilirsiniz.
Para logları gelmiyor
İşlem miktarı belirlediğiniz sınırın altında olabilir:
Kod:
Config.MinimumMoneyLog = 5000
Bütün para işlemlerini görmek için:
Kod:
Config.MinimumMoneyLog = 1
Ancak bu ayar marketteki küçük alışverişleri bile loglayacağı için Discord kanalında çok fazla mesaj oluşturabilir.
Ölüm logları gelmiyor
server.cfg içerisinde baseevents’in başlatıldığını kontrol edin:
Kod:
ensure baseevents
Resource klasörünün şu alanda bulunması gerekir:
Kod:
resources/[system]/baseevents
No such export SendLog hatası
exxdev_discordlogs, log göndermeye çalışan scriptten önce başlamalıdır:
Kod:
ensure exxdev_discordlogs
ensure meslek-scripti
ensure galeri-scripti
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14. Güvenlik Kontrol Listesi
- Webhook adreslerini client.lua dosyasına yazmayın.
- Webhook adreslerini GitHub’da herkese açık paylaşmayın.
- Webhook adreslerini ekran görüntülerinde göstermeyin.
- Log kanallarını normal oyunculara açmayın.
- Bot tokenini webhook adresi yerine kullanmayın.
- Oyuncuların log eventini doğrudan tetiklemesine izin vermeyin.
- Log mesajlarında şifre veya ödeme bilgisi bulundurmayın.
- Webhook sızarsa eskisini silip yenisini oluşturun.
- Her işlem için ayrı log kanalı kullanmayı değerlendirin.
- Discord 429 hatalarını düzenli olarak kontrol edin.
- Resource dosyalarının yedeğini alın.
Not: baseevents ölüm bilgisi istemci tarafından bildirildiği için ölüm loglarını tek başına kesin hile veya ban kanıtı olarak kullanmayın. Bu loglar yönetim incelemesi ve olay takibi içindir.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Sonuç
Bu işlemlerin ardından QBCore FiveM sunucunuzda çalışan temel bir Discord log sistemine sahip olacaksınız.
Sistem otomatik olarak:
- Oyuncu giriş ve çıkışlarını
- Karakter ve Citizen ID bilgilerini
- Belirlenen miktarın üzerindeki para hareketlerini
- Oyuncu ölüm ve öldürme işlemlerini
- Resource başlangıç durumunu
Discord’a gönderecektir.
Item, araç, meslek ve yönetici logları ilgili scriptlerin server dosyalarına verilen örnekler kullanılarak eklenebilir.
Kodlama ve anlatım: ExxDev
Webhook adreslerinizi hiçbir zaman herkese açık olarak paylaşmayın.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Resmî Kaynaklar
