Загрузка данных
-- AdminPunish_kdm1eHvH.lua
-- Система наказаний: mute, gag, ban
-- ============================================================
-- Платформа бана (координаты)
-- ============================================================
local BAN_POS = Vector(6035.70, -936.62, 2983.03)
local BAN_ANG = Angle(2.19, -0.45, 0.00)
-- ============================================================
-- БД
-- ============================================================
local function InitDB()
if sql.TableExists("admin_punishments") then return end
sql.Query([[
CREATE TABLE admin_punishments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
steamid TEXT,
steamid64 TEXT,
nick TEXT,
type TEXT,
reason TEXT,
admin_steamid TEXT,
admin_nick TEXT,
created INTEGER,
expires INTEGER,
active INTEGER DEFAULT 1
)
]])
print("[AdminPunish] Таблица admin_punishments создана")
end
InitDB()
-- ============================================================
-- Парсер времени
-- ============================================================
local function parseTime(str)
if not str or str == "" then return nil end
str = string.lower(str)
if str == "0" then return 0 end -- навсегда
local num = tonumber(string.match(str, "^(%d+)"))
if not num then return nil end
local unit = string.match(str, "%d+([a-z]+)")
if not unit then return num * 60 end -- по умолчанию минуты
if unit == "m" or unit == "min" then return num * 60 end
if unit == "h" or unit == "hour" then return num * 3600 end
if unit == "d" or unit == "day" then return num * 86400 end
if unit == "w" or unit == "week" then return num * 604800 end
if unit == "mo" or unit == "month" then return num * 2592000 end
if unit == "y" or unit == "year" then return num * 31536000 end
return num * 60
end
local function formatTime(seconds)
if seconds == 0 or not seconds then return "навсегда" end
if seconds >= 31536000 then return math.floor(seconds / 31536000) .. "г" end
if seconds >= 2592000 then return math.floor(seconds / 2592000) .. "мес" end
if seconds >= 604800 then return math.floor(seconds / 604800) .. "н" end
if seconds >= 86400 then return math.floor(seconds / 86400) .. "д" end
if seconds >= 3600 then return math.floor(seconds / 3600) .. "ч" end
return math.floor(seconds / 60) .. "м"
end
-- ============================================================
-- Утилиты
-- ============================================================
local function findPlayer(name)
if not name or name == "" then return nil end
local lower = string.lower(name)
for _, p in ipairs(player.GetAll()) do
if IsValid(p) and string.find(string.lower(p:Nick()), lower, 1, true) then
return p
end
end
return nil
end
local function notify(ply, msg)
if IsValid(ply) then
ply:PrintMessage(HUD_PRINTTALK, "[Админ] " .. msg)
else
print("[Админ] " .. msg)
end
end
-- ============================================================
-- Активные наказания (кэш для быстрого доступа)
-- ============================================================
local activePunishments = {} -- [steamid] = { mute = exp, gag = exp, ban = exp }
local function refreshCache(steamid)
if not steamid then return end
activePunishments[steamid] = activePunishments[steamid] or {}
local q = "SELECT type, expires FROM admin_punishments WHERE steamid = " .. sql.SQLStr(steamid) ..
" AND active = 1 AND (expires = 0 OR expires > " .. os.time() .. ")"
local res = sql.Query(q)
activePunishments[steamid] = { mute = nil, gag = nil, ban = nil }
if res then
for _, row in ipairs(res) do
local t = row.type
local exp = tonumber(row.expires) or 0
activePunishments[steamid][t] = exp
end
end
end
-- Публичный доступ
function GetPunishment(ply, type)
if not IsValid(ply) then return nil end
local sid = ply:SteamID()
activePunishments[sid] = activePunishments[sid] or { mute = nil, gag = nil, ban = nil }
local exp = activePunishments[sid][type]
if exp and exp > 0 and exp <= os.time() then
-- Истекло — убираем
sql.Query("UPDATE admin_punishments SET active = 0 WHERE steamid = " .. sql.SQLStr(sid) ..
" AND type = " .. sql.SQLStr(type) .. " AND active = 1")
activePunishments[sid][type] = nil
return nil
end
return exp
end
-- ============================================================
-- Выдать наказание
-- ============================================================
local function addPunishment(target, type, duration, reason, admin)
if not IsValid(target) then return end
local expires = 0
if duration > 0 then
expires = os.time() + duration
end
local adminSteam = IsValid(admin) and admin:SteamID() or "console"
local adminNick = IsValid(admin) and admin:Nick() or "Console"
sql.Query(string.format(
"INSERT INTO admin_punishments (steamid, steamid64, nick, type, reason, admin_steamid, admin_nick, created, expires, active) " ..
"VALUES (%s, %s, %s, %s, %s, %s, %s, %d, %d, 1)",
sql.SQLStr(target:SteamID()),
sql.SQLStr(target:SteamID64()),
sql.SQLStr(target:Nick()),
sql.SQLStr(type),
sql.SQLStr(reason or "не указана"),
sql.SQLStr(adminSteam),
sql.SQLStr(adminNick),
os.time(),
expires
))
refreshCache(target:SteamID())
-- Если бан — телепорт на платформу
if type == "ban" then
timer.Simple(0.5, function()
if IsValid(target) then
target:SetPos(BAN_POS)
target:SetEyeAngles(BAN_ANG)
target:SetNWBool("kdm1e_Banned", true)
target:PrintMessage(HUD_PRINTTALK, "=== ТЫ ЗАБАНЕН ===")
target:PrintMessage(HUD_PRINTTALK, "Админ: " .. adminNick)
target:PrintMessage(HUD_PRINTTALK, "Причина: " .. (reason or "не указана"))
target:PrintMessage(HUD_PRINTTALK, "Срок: " .. (expires == 0 and "навсегда" or os.date("%d.%m.%Y %H:%M", expires)))
end
end)
end
end
-- ============================================================
-- Автоснятие истекших наказаний (каждые 30 сек)
-- ============================================================
timer.Create("AdminPunish_Cleanup", 30, 0, function()
local now = os.time()
sql.Query("UPDATE admin_punishments SET active = 0 WHERE expires > 0 AND expires <= " .. now)
for _, ply in ipairs(player.GetAll()) do
if IsValid(ply) then
refreshCache(ply:SteamID())
end
end
end)
-- ============================================================
-- Кик забаненных при заходе
-- ============================================================
hook.Add("PlayerInitialSpawn", "AdminPunish_BanCheck", function(ply)
timer.Simple(3, function()
if not IsValid(ply) then return end
refreshCache(ply:SteamID())
local banExp = activePunishments[ply:SteamID()] and activePunishments[ply:SteamID()].ban
if banExp then
local expireText = (banExp == 0) and "навсегда" or os.date("%d.%m.%Y %H:%M", banExp)
ply:Kick("Ты забанен. Срок: " .. expireText)
end
end)
end)
-- ============================================================
-- Блокировка действий забаненных
-- ============================================================
hook.Add("PlayerNoClip", "AdminPunish_BanNoClip", function(ply)
if GetPunishment(ply, "ban") then return false end
end)
hook.Add("PlayerSwitchWeapon", "AdminPunish_BanWeapon", function(ply)
if GetPunishment(ply, "ban") then return true end
end)
hook.Add("PlayerSpawn", "AdminPunish_BanSpawn", function(ply)
if GetPunishment(ply, "ban") then
timer.Simple(0.1, function()
if IsValid(ply) then
ply:SetPos(BAN_POS)
ply:SetEyeAngles(BAN_ANG)
ply:SetNWBool("kdm1e_Banned", true)
end
end)
end
end)
-- Запрет стрельбы для забаненных
hook.Add("EntityFireBullets", "AdminPunish_BanShoot", function(ent, data)
if IsValid(ent) and ent:IsPlayer() and GetPunishment(ent, "ban") then
return false
end
end)
-- ============================================================
-- КОМАНДЫ
-- ============================================================
-- !mute
concommand.Add("kdm1e_mute", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canMute") then
notify(ply, "Нет прав!")
return
end
if #args < 2 then notify(ply, "!mute Ник время причина"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
local duration = parseTime(args[2])
if not duration then notify(ply, "Неверный формат времени"); return end
table.remove(args, 1); table.remove(args, 1)
local reason = table.concat(args, " ")
addPunishment(target, "mute", duration, reason, ply)
notify(ply, target:Nick() .. " замучен чата на " .. formatTime(duration))
target:PrintMessage(HUD_PRINTTALK, "Ты замучен чата: " .. reason)
end)
-- !unmute
concommand.Add("kdm1e_unmute", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canMute") then return end
if #args < 1 then notify(ply, "!unmute Ник"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
sql.Query("UPDATE admin_punishments SET active = 0 WHERE steamid = " .. sql.SQLStr(target:SteamID()) .. " AND type = 'mute' AND active = 1")
refreshCache(target:SteamID())
notify(ply, target:Nick() .. " размучен")
end)
-- !gag
concommand.Add("kdm1e_gag", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canMute") then return end
if #args < 2 then notify(ply, "!gag Ник время причина"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
local duration = parseTime(args[2])
if not duration then notify(ply, "Неверный формат времени"); return end
table.remove(args, 1); table.remove(args, 1)
local reason = table.concat(args, " ")
addPunishment(target, "gag", duration, reason, ply)
notify(ply, target:Nick() .. " заглушён на " .. formatTime(duration))
target:PrintMessage(HUD_PRINTTALK, "Твой голос заглушён: " .. reason)
end)
-- !ungag
concommand.Add("kdm1e_ungag", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canMute") then return end
if #args < 1 then notify(ply, "!ungag Ник"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
sql.Query("UPDATE admin_punishments SET active = 0 WHERE steamid = " .. sql.SQLStr(target:SteamID()) .. " AND type = 'gag' AND active = 1")
refreshCache(target:SteamID())
notify(ply, target:Nick() .. " разглушён")
end)
-- !ban
concommand.Add("kdm1e_ban", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canBan") then
notify(ply, "Нет прав!")
return
end
if #args < 3 then notify(ply, "!ban Ник время причина"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
local duration = parseTime(args[2])
if not duration then notify(ply, "Неверный формат времени"); return end
table.remove(args, 1); table.remove(args, 1)
local reason = table.concat(args, " ")
addPunishment(target, "ban", duration, reason, ply)
notify(ply, target:Nick() .. " забанен на " .. formatTime(duration))
-- Телепорт на платформу
timer.Simple(1, function()
if IsValid(target) then
target:SetPos(BAN_POS)
target:SetEyeAngles(BAN_ANG)
end
end)
end)
-- !unban
concommand.Add("kdm1e_unban", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canUnban") then
notify(ply, "Нет прав!")
return
end
if #args < 1 then notify(ply, "!unban SteamID"); return end
local sid = args[1]
sql.Query("UPDATE admin_punishments SET active = 0 WHERE (steamid = " .. sql.SQLStr(sid) ..
" OR steamid64 = " .. sql.SQLStr(sid) .. ") AND type = 'ban' AND active = 1")
notify(ply, "Игрок " .. sid .. " разбанен")
-- Если онлайн — снять с платформы
for _, p in ipairs(player.GetAll()) do
if IsValid(p) and (p:SteamID() == sid or p:SteamID64() == sid) then
p:SetNWBool("kdm1e_Banned", false)
refreshCache(p:SteamID())
p:PrintMessage(HUD_PRINTTALK, "Ты разбанен!")
end
end
end)
-- !banlist
concommand.Add("kdm1e_banlist", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canBan") then return end
local res = sql.Query("SELECT nick, steamid, reason, expires FROM admin_punishments WHERE type = 'ban' AND active = 1 ORDER BY created DESC LIMIT 20")
if not res or #res == 0 then
notify(ply, "Список банов пуст")
return
end
notify(ply, "=== СПИСОК БАНОВ ===")
for _, row in ipairs(res) do
local timeText = (tonumber(row.expires) == 0) and "навсегда" or os.date("%d.%m %H:%M", tonumber(row.expires))
notify(ply, string.format("%s | %s | %s | до %s", row.nick, row.steamid, row.reason, timeText))
end
end)
-- !kick
concommand.Add("kdm1e_kick", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canKick") then
notify(ply, "Нет прав!")
return
end
if #args < 2 then notify(ply, "!kick Ник причина"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
table.remove(args, 1)
local reason = table.concat(args, " ")
target:Kick("Кик: " .. reason)
notify(ply, target:Nick() .. " кикнут: " .. reason)
end)
-- !slay
concommand.Add("kdm1e_slay", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canSlay") then
notify(ply, "Нет прав!")
return
end
if #args < 1 then notify(ply, "!slay Ник"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
target:Kill()
notify(ply, target:Nick() .. " убит")
end)
-- !respawn
concommand.Add("kdm1e_respawn", function(ply, cmd, args)
if IsValid(ply) and not RANKS.HasPerm(ply, "canSlay") then return end
if #args < 1 then notify(ply, "!respawn Ник"); return end
local target = findPlayer(args[1])
if not IsValid(target) then notify(ply, "Игрок не найден"); return end
if target:Alive() then target:KillSilent() end
target:Spawn()
notify(ply, target:Nick() .. " заспавнен")
end)
-- ============================================================
-- Чат-команды с !
-- ============================================================
hook.Add("PlayerSay", "AdminPunish_ChatCommands", function(ply, text, team)
if not IsValid(ply) then return end
local lower = string.lower(text)
local cmds = { "mute", "unmute", "gag", "ungag", "ban", "unban", "banlist", "kick", "slay" }
for _, c in ipairs(cmds) do
if lower == "!" .. c or lower == "/" .. c then
ply:ConCommand("kdm1e_" .. c)
return ""
end
if string.StartWith(lower, "!" .. c .. " ") or string.StartWith(lower, "/" .. c .. " ") then
local args = string.Explode(" ", text)
table.remove(args, 1)
ply:ConCommand("kdm1e_" .. c .. " " .. table.concat(args, " "))
return ""
end
end
end)
print("[AdminPunish] Система наказаний загружена")