diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau index c38d42b7e..150654bf7 100644 --- a/MainModule/Server/Commands/Admins.luau +++ b/MainModule/Server/Commands/Admins.luau @@ -1,1719 +1,1598 @@ -server = nil -service = nil -Routine = nil -GetEnv = nil -origEnv = nil -logError = nil - ---// Admin -return function(Vargs, GetEnv) - local env = GetEnv(nil, {script = script}) - setfenv(1, env) - +--!nocheck +return function(Vargs, env) local server = Vargs.Server; local service = Vargs.Service; - local Functions, Admin, Anti, Core, HTTP, Logs, Remote, Process, Variables, Settings, Commands - local AddLog, TrackTask, Defaults - local CreatorId = game.CreatorType == Enum.CreatorType.User and game.CreatorId or service.GetGroupCreatorId(game.CreatorId) - local function Init() - Functions = server.Functions; - Admin = server.Admin; - Anti = server.Anti; - Core = server.Core; - HTTP = server.HTTP; - Logs = server.Logs; - Remote = server.Remote; - Process = server.Process; - Variables = server.Variables; - Settings = server.Settings; - Commands = server.Commands; - Defaults = server.Defaults - - TrackTask = service.TrackTask - AddLog = Logs.AddLog; - - TrackTask("Thread: ChatServiceHandler", function() - - --// Support for modern TextChatService - if service.TextChatService and service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService then - local function onNewTextchannel(textchannel: TextChannel) - AddLog("Script", `Connected to TextChannel: {textchannel.Name}`) - - if Settings.OverrideChatCallbacks ~= false then --// Default to "on" this for all games - AddLog("Script", "Overriding ShouldDeliverCallback for " .. textchannel.Name) - textchannel.ShouldDeliverCallback = function(chatMessage, textSource) - if - chatMessage.Status == Enum.TextChatMessageStatus.Success - or chatMessage.Status == Enum.TextChatMessageStatus.Sending - then - local SenderId = chatMessage.TextSource.UserId - local SenderPlayer = service.Players:GetPlayerByUserId(SenderId) - local Receiver = service.Players:GetPlayerByUserId(textSource.UserId) - local slowCache = Admin.SlowCache - - local IsOriginalSender = SenderPlayer == Receiver - - if not SenderPlayer then - return true - elseif Admin.DoHideChatCmd(SenderPlayer, chatMessage.Text) then -- // Hide chat commands? - return false - elseif Admin.IsMuted(SenderPlayer) then -- // Mute handler - if IsOriginalSender then - server.Remote.Send(SenderPlayer, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are muted! Other players cannot see your messages.`) - end - - return false - elseif Admin.SlowMode and not Admin.CheckAdmin(SenderPlayer) and slowCache[SenderPlayer] and os.time() - slowCache[SenderPlayer] < Admin.SlowMode then - if IsOriginalSender then --// Only show this for the person sending! Hide for others, however - --Functions.Notification("You are chatting too fast!", string.format("[Adonis] :: Slow mode enabled! (%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[SenderPlayer])), {SenderPlayer}, 10) - - server.Remote.Send(SenderPlayer, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are sending messages too fast! {string.format("(%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[SenderPlayer]))}`) - end - - return false - end - - if Variables.DisguiseBindings[SenderId] then -- // Disguise command handler - chatMessage.PrefixText = Variables.DisguiseBindings[SenderId].TargetUsername..":" - end - - if Admin.SlowMode and IsOriginalSender then - slowCache[SenderPlayer] = os.time() - end - end - - return true + local Settings = server.Settings + local Functions, Commands, Admin, Anti, Core, HTTP, Logs, Remote, Process, Variables, Deps = + server.Functions, server.Commands, server.Admin, server.Anti, server.Core, server.HTTP, server.Logs, server.Remote, server.Process, server.Variables, server.Deps + + if env then setfenv(1, env) end + + local Routine = env.Routine + + return { + SetRank = { + Prefix = Settings.Prefix; + Commands = {"setrank", "permrank", "permsetrank"}; + Args = {"player/user", "rank"}; + Description = "Sets the admin rank of the target user(s); THIS SAVES!"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + assert(args[1], "Missing target user (argument #1)") + local rankName = assert(args[2], "Missing rank name (argument #2)") + + local newRank = Settings.Ranks[rankName] + if not newRank then + for thisRankName, thisRank in Settings.Ranks do + if thisRankName:lower() == rankName:lower() then + rankName = thisRankName + newRank = thisRank + break end + end + end + assert(newRank, `No rank named '{rankName}' exists`) + + local newLevel = newRank.Level + local senderLevel = data.PlayerData.Level + + assert(newLevel < senderLevel, string.format("Rank level (%s) cannot be equal to or above your own level (%s)", newLevel, senderLevel)) + local v = Functions.GetPlayers(plr, args[1], {NoFakePlayer = false}) + for _, p in v do + if senderLevel > Admin.GetLevel(p) then + Admin.AddAdmin(p, rankName) + Functions.LogAdminAction(plr, "Set Rank", p.Name, `New rank: {rankName}, New level: {newLevel}`) + Functions.Notification( + "Notification", + `You are {if string.lower(string.sub(rankName, 1, 3)) == "the" then "" elseif string.match(rankName, "^[AEIOUaeiou]") and string.lower(string.sub(rankName, 1, 3)) ~= "uni" then "an " else "a "}{rankName}. Click to view commands.`, + {p}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`) + ) + Functions.Hint(`{service.FormatPlayer(p, true)} is now rank {rankName} (Permission Level: {newLevel})`, {plr}) else - AddLog("Script", `Using the 'CanSend' method of handling chat connectivity in channel {textchannel.Name}`) - server.Variables.TextChatSpeakers = {} - local function AddUserToTextChatSpeakers(player: Player, speaker: TextSource) - if not server.Variables.TextChatSpeakers[player] then - server.Variables.TextChatSpeakers[player] = {} - end - table.insert(server.Variables.TextChatSpeakers[player], speaker) - --// Check if the player is muted or not - speaker:SetAttribute("OriginalCanSend", speaker.CanSend) - if server.Admin.IsMuted(player) then - speaker.CanSend = false - end - end - local function SpeakerAdded(speaker: TextSource) - if speaker.UserId and speaker.UserId > 0 then - local Player = service.Players:GetPlayerByUserId(speaker.UserId) - if Player then - AddUserToTextChatSpeakers(Player, speaker) - end - end - end - local function SpeakerRemoved(speaker: TextSource) - if speaker.UserId and speaker.UserId > 0 then - local Player = service.Players:GetPlayerByUserId(speaker.UserId) - local Tab = server.Variables.TextChatSpeakers[Player] - if Tab then - local index = table.find(Tab, speaker) - while index do - table.remove(Tab, index) - index = table.find(Tab, speaker) - end - task.defer(function() - if #Tab == 0 then - server.Variables.TextChatSpeakers[Player] = nil - end - end) - end - end - end - - textchannel.ChildAdded:Connect(function(textSource) - if textSource:IsA("TextSource") then - SpeakerAdded(textSource) - end - end) - - textchannel.ChildRemoved:Connect(function(textSource) - if textSource:IsA("TextSource") then - SpeakerRemoved(textSource) - end - end) - - for _,inst in textchannel:GetChildren() do - if inst:IsA("TextSource") then - SpeakerAdded(inst) - end - end - + Functions.Hint(`You do not have permission to set the rank of {service.FormatPlayer(p, true)}`, {plr}) end end - - --// Only set this up once - --// This is for commands to tell us when a player should be muted - if not Settings.OverrideChatCallbacks then - service.Events.PlayerMuted:Connect(function(data) - local PlayerId = data.Target; - local ModId = data.Moderator; - - local Player = service.Players:GetPlayerByUserId(PlayerId) - --// Loop through CanSend of a speaker - for _,speakers : TextSource in if Player then server.Variables.TextChatSpeakers[Player] or {} else {} do - speakers.CanSend = false - end - if Player then - AddLog("Script", `Muted player {Player.Name}:{Player.UserId} using CanSend method`) - end - end) - service.Events.PlayerUnMuted:Connect(function(data) - local PlayerId = data.Target; - local ModId = data.Moderator; - - local Player = service.Players:GetPlayerByUserId(PlayerId) - --// Loop through CanSend of a speaker - for _,speakers : TextSource in if Player then server.Variables.TextChatSpeakers[Player] or {} else {} do - local original = speakers:GetAttribute("OriginalCanSend") - speakers.CanSend = if original ~= nil then original else true - end - if Player then - AddLog("Script", `UnMuted player {Player.Name}:{Player.UserId} via CanSend method`) + end; + }; + + SetTempRank = { + Prefix = Settings.Prefix; + Commands = {"settemprank", "temprank", "tempsetrank"}; + Args = {"player", "rank"}; + Description = `Identical to {Settings.Prefix}setrank, but doesn't save`; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + assert(args[1], "Missing target player (argument #1)") + local rankName = assert(args[2], "Missing rank name (argument #2)") + + local newRank = Settings.Ranks[rankName] + if not newRank then + for thisRankName, thisRank in Settings.Ranks do + if thisRankName:lower() == rankName:lower() then + rankName = thisRankName + newRank = thisRank + break end - end) - service.Events.MutedPlayerChat_UnFiltered:Connect(function(p, ...) - server.Remote.Send(p, "Function", "DisplaySystemMessageInTextChat", nil, `[Adonis Chat]: You are muted! Other players cannot see your messages.`) - end) + end end + assert(newRank, `No rank named '{rankName}' exists`) + local newLevel = newRank.Level + local senderLevel = data.PlayerData.Level - local function onTextChannelsAdded(textChannels) - textChannels.ChildAdded:Connect(function(child) - if child:IsA("TextChannel") then - task.spawn(onNewTextchannel, child) - end - end) + assert(newLevel < senderLevel, string.format("Rank level (%s) cannot be equal to or above your own level (%s)", newLevel, senderLevel)) - for _, v in textChannels:GetChildren() do - if v:IsA("TextChannel") then - task.spawn(onNewTextchannel, v) - end + for _, v in service.GetPlayers(plr, args[1]) do + if senderLevel > Admin.GetLevel(v) then + Admin.AddAdmin(v, rankName, true) + Functions.LogAdminAction(plr, "Set Temporary Rank", v.Name, `Temporary rank set to: {rankName}`) + Functions.Notification("Notification", `You are a temp {rankName}. Click to view commands.`, {v}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`)) + Functions.Hint(`{service.FormatPlayer(v, true)} is now rank {rankName} (Permission Level: {newLevel})`, {plr}) + else + Functions.Hint(`You do not have permission to set the rank of {service.FormatPlayer(v, true)}`, {plr}) end end - - service.TextChatService.ChildAdded:Connect(function(child) - if child.Name == "TextChannels" then - task.spawn(onTextChannelsAdded, child) + end; + }; + + SetLevel = { + Prefix = Settings.Prefix; + Commands = {"setlevel", "setadminlevel"}; + Args = {"player", "level"}; + Description = "Sets the target player(s) permission level for the current server; does not save"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + local senderLevel = data.PlayerData.Level + local newLevel = assert(tonumber(args[2]), "Level must be a number") + + assert(newLevel < senderLevel, `Level cannot be equal to or above your own permission level ({senderLevel})`); + + for _, v in service.GetPlayers(plr, args[1])do + if senderLevel > Admin.GetLevel(v) then + Admin.SetLevel(v, newLevel, args[3] == "true") + Functions.LogAdminAction(plr, "Set Level", v.Name, `New level: {newLevel}`) + Functions.Notification("Notification", `Your admin permission level was set to {newLevel} for this server only. Click to view commands.`, {v}, 10, "MatIcon://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`)) + Functions.Hint(`{service.FormatPlayer(v, true)} is now permission level {newLevel}`, {plr}) + else + Functions.Hint(`You do not have permission to set the permission level of {service.FormatPlayer(v, true)}`, {plr}) end - end) - - if service.TextChatService:FindFirstChild("TextChannels") then - task.spawn(pcall, onTextChannelsAdded, service.TextChatService:FindFirstChild("TextChannels")) end - - AddLog("Script", "TextChatService Handler Loaded") - end - - --// Support for legacy Lua chat system - --// ChatService mute handler (credit to Coasterteam) - AddLog("Script", "Starting loading of legacy chatservice handler") - local chatService = Functions.GetChatService(300) - if chatService then - chatService:RegisterProcessCommandsFunction("ADONIS_CMD", function(speakerName, message) - local speaker = chatService:GetSpeaker(speakerName) - local speakerPlayer = speaker and speaker:GetPlayer() - - if not speakerPlayer then - return false - end - - if Admin.DoHideChatCmd(speakerPlayer, message) then - return true + end; + }; + + UnAdmin = { + Prefix = Settings.Prefix; + Commands = {"unadmin", "unmod", "unowner", "unpadmin", "unheadadmin", "unrank"}; + Args = {"player/user / list entry", "temp? (true/false) (default: false)"}; + Description = "Removes admin/moderator ranks from the target player(s); saves unless is 'true'"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + local target = assert(args[1], "Missing target user (argument #1)") + local temp = args[2] and args[2]:lower() == "true" + local senderLevel = data.PlayerData.Level + local userFound = false + + if not string.find(target, ":") then + for _, v in service.GetPlayers(plr, target, { + UseFakePlayer = true; + DontError = true; + }) + do + userFound = true + local targLevel, targRank = Admin.GetLevel(v) + if targLevel > 0 then + if senderLevel > targLevel then + Admin.RemoveAdmin(v, temp) + Functions.LogAdminAction(plr, "Remove Admin", v.Name, `Temporary: {temp}`) + Functions.Hint(string.format("Removed %s from rank %s", service.FormatPlayer(v, true), targRank or "[unknown rank]"), {plr}) + Functions.Notification("Notification", `You are no longer a(n) {targRank or "admin"}`, {v}, 10, "MatIcon://Shield") + else + Functions.Hint(`You do not have permission to remove {service.FormatPlayer(v, true)}'s rank`, {plr}) + end + else + Functions.Hint(`{service.FormatPlayer(v, true)} does not already have any rank to remove`, {plr}) + end end - return false - end) - - chatService:RegisterProcessCommandsFunction("ADONIS_MUTE_SERVER", function(speakerName, _, channelName) - local slowCache = Admin.SlowCache - - local speaker = chatService:GetSpeaker(speakerName) - local speakerPlayer = speaker and speaker:GetPlayer() - - if not speakerPlayer then - return false + if userFound then + return + else + Functions.Hint("User not found in server; searching datastore", {plr}) end + end - if speakerPlayer and Admin.IsMuted(speakerPlayer) then - speaker:SendSystemMessage("[Adonis] :: You are muted!", channelName) - return true - elseif speakerPlayer and Admin.SlowMode and not Admin.CheckAdmin(speakerPlayer) and slowCache[speakerPlayer] and os.time() - slowCache[speakerPlayer] < Admin.SlowMode then - speaker:SendSystemMessage(string.format("[Adonis] :: Slow mode enabled! (%g second(s) remaining)", Admin.SlowMode - (os.time() - slowCache[speakerPlayer])), channelName) - return true + for rankName, rankData in Settings.Ranks do + if senderLevel <= rankData.Level then + continue end - - if Admin.SlowMode then - slowCache[speakerPlayer] = os.time() + for i, user in rankData.Users do + if not (user:lower() == target:lower() or user:lower():match(`^{target:lower()}:`) or Admin.DoCheck(target, user)) then + continue + end + if + Remote.GetGui(plr, "YesNoPrompt", { + Question = `Remove '{user}' from '{rankName}'?`; + }) == "Yes" + then + table.remove(rankData.Users, i) + if not temp and Settings.SaveAdmins then + service.TrackTask("Thread: RemoveAdmin", Core.DoSave, false, { + Type = "TableRemove"; + Table = {"Settings", "Ranks", rankName, "Users"}; + Value = user; + }); + Functions.Hint(`Removed entry '{user}' from {rankName}`, {plr}) + Logs:AddLog("Script", `{plr} removed {user} from {rankName}`) + end + end + userFound = true end - - return false - end) - - AddLog("Script", "ChatService Handler Loaded") - elseif chatService == false then - AddLog("Script", "Using TextChatService; Handler Loaded") - else - warn("Place is missing ChatService; Vanilla Roblox chat related features may not work") - AddLog("Script", "ChatService Handler Not Found") - end - end) - - --// Make sure the default ranks are always present for compatability with existing commands - local Ranks = Settings.Ranks - for rank, data in Defaults.Settings.Ranks do - if not Ranks[rank] then - for r, d in Ranks do - if d.Level == data.Level then - data.Hidden = true - break + end + assert(userFound, `No table entries matching '{args[1]}' were found`) + end + }; + + TempUnAdmin = { + Prefix = Settings.Prefix; + Commands = {"tempunadmin", "untempadmin", "tunadmin", "untadmin"}; + Args = {"player"}; + Description = "Removes the target players' admin powers for this server; does not save"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + local senderLevel = data.PlayerData.Level + + for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)")) do + local targetLevel = Admin.GetLevel(v) + if targetLevel > 0 then + if senderLevel > targetLevel then + Admin.RemoveAdmin(v, true) + Functions.LogAdminAction(plr, "Temporary Unadmin", v.Name, "Admin powers temporarily removed") + Functions.Hint(`Removed {service.FormatPlayer(v)}'s admin powers`, {plr}) + Functions.Notification("Notification", "Your admin powers have been temporarily removed", {v}, 10, "MatIcons://Remove moderator") + else + Functions.Hint(`You do not have permission to remove {service.FormatPlayer(v, true)}'s admin powers`, {plr}) + end + else + Functions.Hint(`{service.FormatPlayer(v, true)} is not an admin`, {plr}) end end - Ranks[rank] = data end - end - - if Settings.CustomRanks then - local Ranks = Settings.Ranks - for name, users in Settings.CustomRanks do - if not Ranks[name] then - Ranks[name] = { - Level = 1; - Users = users; - }; + }; + + TempModerator = { + Prefix = Settings.Prefix; + Commands = {"tempmod", "tmod", "tempmoderator", "tmoderator"}; + Args = {"player"}; + Description = "Makes the target player(s) a temporary moderator; does not save"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + local senderLevel = data.PlayerData.Level + + for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)")) do + if senderLevel > Admin.GetLevel(v) then + Admin.AddAdmin(v, "Moderators", true) + Functions.LogAdminAction(plr, "Temporary Moderator", v.Name, "N/A") + Functions.Notification("Notification", "You are a temp moderator. Click to view commands.", {v}, 10, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`)) + Functions.Hint(`{service.FormatPlayer(v, true)} is now a temp moderator`, {plr}) + else + Functions.Hint(`{service.FormatPlayer(v, true)} is already the same admin level as you or higher`, {plr}) + end end end - end + }; - if Settings.CommandCooldowns then - for cmdName, cooldownData in Settings.CommandCooldowns do - local realCmd = Admin.GetCommand(cmdName) + Moderator = { + Prefix = Settings.Prefix; + Commands = {"permmod", "pmod", "mod", "moderator", "pmoderator"}; + Args = {"player/user"}; + Description = "Makes the target player(s) a moderator; saves"; + AdminLevel = "Admins"; + Dangerous = true; + Function = function(plr: Player, args: {string}, data: {any}) + local senderLevel = data.PlayerData.Level - if realCmd then - if cooldownData.Player then - realCmd.PlayerCooldown = cooldownData.Player - end - - if cooldownData.Server then - realCmd.ServerCooldown = cooldownData.Server - end - - if cooldownData.Cross then - realCmd.CrossCooldown = cooldownData.Cross + for _, v in service.GetPlayers(plr, assert(args[1], "Missing target player (argument #1)"), { + UseFakePlayer = true; + }) + do + if senderLevel > Admin.GetLevel(v) then + Admin.AddAdmin(v, "Moderators") + Functions.LogAdminAction(plr, "Promoted to Moderator", v.Name, "N/A") + Functions.Notification("Notification", "You are a moderator. Click to view commands.", {v}, 10, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}cmds')`)) + Functions.Hint(`{service.FormatPlayer(v, true)} is now a moderator`, {plr}) + else + Functions.Hint(`{service.FormatPlayer(v, true)} is already the same admin level as you or higher`, {plr}) end end end - end - - Admin.Init = nil; - AddLog("Script", "Admin Module Initialized") - end; - - local function RunAfterPlugins(data) - --// Backup Map - if Settings.AutoBackup then - TrackTask("Thread: Initial Map Backup", Admin.RunCommand, false, `{Settings.Prefix}backupmap`) - end - - --// Run OnStartup Commands - for i,v in Settings.OnStartup do - print(`Running startup command {v}`) - TrackTask(`Thread: Startup_Cmd: {v}`, Admin.RunCommand, false, v) - AddLog("Script", { - Text = `Startup: Executed {v}`; - Desc = `Executed startup command; {v}`; - }) - end - - --// Check if Shutdownlogs is set and if not then set it - if Core.DataStore and not Core.GetData("ShutdownLogs") then - Core.SetData("ShutdownLogs", {}) - end - - Admin.RunAfterPlugins = nil; - AddLog("Script", "Admin Module RunAfterPlugins Finished") - end - - service.MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, id, purchased) - if Variables and player.Parent and table.find(Variables.DonorPass, id) and purchased then - Variables.CachedDonors[tostring(player.UserId)] = os.time() - end - end) - - local function stripArgPlaceholders(alias) - return service.Trim(string.gsub(alias, "<%S+>", "")) - end - - local function FormatAliasArgs(alias, aliasCmd, msg) - local uniqueArgs = {} - local argTab = {} - local numArgs = 0 - - --// First try to extract args info from the alias - for arg in string.gmatch(alias, "<(%S+)>") do - if arg ~= "" and arg ~= " " then - local arg = `<{arg}>` - if not uniqueArgs[arg] then - numArgs += 1 - uniqueArgs[arg] = true - table.insert(argTab, arg) - end - end - end - - --// If no args in alias string, check the command string instead and try to guess args based on order of appearance - if numArgs == 0 then - for arg in string.gmatch(aliasCmd, "<(%S+)>") do - if arg ~= "" and arg ~= " " then - local arg = `<{arg}>` - if not uniqueArgs[arg] then --// Get only unique placeholder args, repeats will be matched to the same arg pos - numArgs += 1 - uniqueArgs[arg] = true --// :cmd - table.insert(argTab, arg) + }; + + Broadcast = { + Prefix = Settings.Prefix; + Commands = {"broadcast", "bc"}; + Args = {"Message"}; + Filter = true; + Description = "Makes a message in the chat window"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers() do + if service.TextChatService and service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService then + local TextToUse = args[1] + if data.Options.Chat ~= true then + TextToUse = service.SanitizeXML(args[1] or "Hello world!") + end + Remote.Send( + v, "Function", "DisplaySystemMessageInTextChat", nil, `{ + string.format(`[%s] %s`, Settings.SystemTitle, service.Filter(TextToUse), plr, v) + }`) + else + Remote.Send(v, "Function", "ChatMessage", string.format("[%s] %s", Settings.SystemTitle, service.Filter(args[1], plr, v)), Color3.fromRGB(255,64,77)) end end end - end - - local suppliedArgs = Admin.GetArgs(msg, numArgs) -- User supplied args (when running :alias arg) - local out = aliasCmd - - local SanitizePattern = service.SanitizePattern - for i,argType in argTab do - local replaceWith = suppliedArgs[i] - if replaceWith then - out = string.gsub(out, SanitizePattern(argType), replaceWith) - end - end - - return out - end + }; - server.Admin = { - Init = Init; - RunAfterPlugins = RunAfterPlugins; - - SpecialLevels = {}; - TempAdmins = {}; - - PrefixCache = {}; - CommandCache = {}; - SlowCache = {}; - UserIdCache = {}; - UsernameCache = {}; - GroupsCache = {}; - - BlankPrefix = false; - - --// How long admin levels will be cached (unless forcibly updated via something like :admin user) - AdminLevelCacheTimeout = 30; - - CheckSlowMode = function(p: Player) - if Admin.SlowMode and Admin.SlowCache[p] and os.time() - Admin.SlowCache[p] < Admin.SlowMode then - return true - else - return false + ShutdownLogs = { + Prefix = Settings.Prefix; + Commands = {"shutdownlogs", "shutdownlog", "slogs", "shutdowns"}; + Args = {}; + Description = "Shows who shutdown or restarted a server and when"; + AdminLevel = "Admins"; + ListUpdater = function(plr: Player) + local logs = Core.GetData("ShutdownLogs") or {} + local tab = {} + for i, v in logs do + if v.Restart then v.Time ..= " [RESTART]" end + tab[i] = { + Text = `{v.Time}: {v.User}`; + Desc = `Reason: {v.Reason}`; + } + end + return tab + end; + Function = function(plr: Player, args: {string}) + Remote.MakeGui(plr, "List", { + Title = "Shutdown Logs"; + Table = Logs.ListUpdaters.ShutdownLogs(plr); + Update = "ShutdownLogs"; + }) end - end, - - DoHideChatCmd = function(p: Player, message: string, data: {[string]: any}?) - local pData = data or Core.GetPlayer(p) - if pData.Client.HideChatCommands then - if Admin.BlankPrefix and - - (string.sub(message,1,1) ~= Settings.Prefix or string.sub(message,1,1) ~= Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and not table.find(Settings.Prefix, string.sub(message,1,1)))) then - local isCMD = Admin.GetCommand(message) - if isCMD then - return true + }; + + ServerLock = { + Prefix = Settings.Prefix; + Commands = {"slock", "serverlock", "lockserver"}; + Args = {"on/off"}; + Description = "Enables/disables server lock"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local arg = args[1] and string.lower(args[1]) + Functions.LogAdminAction(plr, "Server Lock", "Server is now " .. (if (arg == "on" or arg == "true") then "locked" else "unlocked"), "") + if (not arg and Variables.ServerLock ~= true) or arg == "on" or arg == "true" then + Variables.ServerLock = true + Functions.Hint("Server Locked", service.Players:GetPlayers()) + elseif Variables.ServerLock == true or arg == "off" or arg == "false" then + Variables.ServerLock = false + Functions.Hint("Server Unlocked", service.Players:GetPlayers()) + end + end + }; + + Whitelist = { + Prefix = Settings.Prefix; + Commands = {"wl", "enablewhitelist", "whitelist"}; + Args = {"on/off/add/remove/list/clear", "optional player"}; + Description = "Enables/disables the server whitelist; :wl username to add them to the whitelist"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local sub = string.lower(args[1]) + + if sub == "on" or sub == "enable" then + Variables.Whitelist.Enabled = true + Functions.Hint("Enabled server whitelist", service.Players:GetPlayers()) + elseif sub == "off" or sub == "disable" then + Variables.Whitelist.Enabled = false + Functions.Hint("Disabled server whitelist", service.Players:GetPlayers()) + elseif sub == "add" then + if args[2] then + local plrs = service.GetPlayers(plr, args[2], { + DontError = true; + IsServer = false; + IsKicking = false; + NoFakePlayer = false; + }) + if #plrs>0 then + for _, v in plrs do + table.insert(Variables.Whitelist.Lists.Settings, `{v.Name}:{v.UserId}`) + Functions.Hint(`Added {service.FormatPlayer(v)} to the whitelist`, {plr}) + end + else + table.insert(Variables.Whitelist.Lists.Settings, args[2]) + end else - return false + error("Missing user argument") end - elseif (string.sub(message,1,1) == Settings.Prefix or string.sub(message,1,1) == Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and table.find(Settings.Prefix, string.sub(message,1,1)))) - and string.sub(message,2,2) ~= string.sub(message,1,1) then - return true; - end - end - end; - - GetPlayerGroups = function(p: Player) - if not p or p.Parent ~= service.Players then - return {} - end - return Admin.GetGroups(p.UserId) - end; - - GetPlayerGroup = function(p, group) - local groups = Admin.GetPlayerGroups(p) - local isId = type(group) == "number" - if groups and #groups > 0 then - for _, g in groups do - if (isId and g.Id == group) or (not isId and g.Name == group) then - return g + elseif sub == "remove" then + if args[2] then + for i, v in Variables.Whitelist.Lists.Settings do + if string.sub(string.lower(v), 1,#args[2]) == string.lower(args[2])then + table.remove(Variables.Whitelist.Lists.Settings,i) + Functions.Hint(`Removed {v} from the whitelist`, {plr}) + end + end + else + error("Missing user argument") end - end - end - end; - - GetGroups = function(uid, updateCache) - uid = tonumber(uid) - - if type(uid) == "number" then - local existCache = Admin.GroupsCache[uid] - local canUpdate = false - - if not updateCache then - --> Feel free to adjust the time to update over or less than 300 seconds (5 minutes). - --> 300 seconds is recommended in the event of unexpected server breakdowns with Roblox and faster performance. - if (existCache and (os.time()-existCache.LastUpdated > 300)) or not existCache then - canUpdate = true + elseif sub == "list" then + local Tab = {} + for Key, List in Variables.Whitelist.Lists do + local Prefix = Key == "Settings" and "" or `[{Key}] ` + for _, User in List do + table.insert(Tab, {Text = Prefix .. User, Desc = User}) + end end + Remote.MakeGui(plr, "List", {Title = "Whitelist List"; Tab = Tab;}) + elseif sub == "clear" then + Variables.Whitelist.Lists.Settings = {} + Functions.Hint("Cleared server whitelist", service.Players:GetPlayers()) else - canUpdate = true - end - - if canUpdate then - local cacheTab = { - Groups = (existCache and existCache.Groups) or {}; - LastUpdated = os.time(); - } - Admin.GroupsCache[uid] = cacheTab - - local suc,groups = pcall(function() - return service.GroupService:GetGroupsAsync(uid) or {} - end) - - if suc and type(groups) == "table" then - cacheTab.Groups = groups - return cacheTab.Groups + error("Invalid subcommand (on/off/add/remove/list/clear)") + end + end + }; + + SystemNotify = { + Prefix = Settings.Prefix; + Commands = {"sn", "systemnotify", "sysnotif", "sysnotify", "systemsmallmessage", "snmessage", "snmsg", "ssmsg", "ssmessage"}; + Args = {"message"}; + Filter = true; + Description = "Makes a system small message"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[1], "Missing message") + for _, v in service.GetPlayers() do + Remote.RemoveGui(v, "Notify") + Functions.Notify(Settings.SystemTitle, service.Filter(args[1], plr, v), {v}) + end + end + }; + + Notif = { + Prefix = Settings.Prefix; + Commands = {"setmessage", "notif", "setmsg", "permhint"}; + Args = {"message OR off"}; + Filter = true; + Description = "Sets a small hint message at the top of the screen"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[1], "Missing message (or enter 'off' to disable)") + + if args[1] == "off" or args[1] == "false" then + Variables.NotifMessage = nil + for _, v in service.GetPlayers() do + Remote.RemoveGui(v, "Notif") end - - Admin.GroupsCache[uid] = cacheTab - return table.clone(cacheTab.Groups) else - return table.clone((existCache and existCache.Groups) or {}) - end - end - end; - - GetGroupLevel = function(uid, groupId) - groupId = tonumber(groupId) - - if groupId then - local groups = Admin.GetGroups(uid) or {} - - for _, group in groups do - if group.Id == groupId then - return group.Rank + Variables.NotifMessage = args[1] + for _, v in service.GetPlayers() do + Remote.MakeGui(v, "Notif", { + Message = Variables.NotifMessage; + }) end end end - - return 0 - end; - - CheckInGroup = function(uid, groupId) - local groups = Admin.GetGroups(uid) or {} - groupId = tonumber(groupId) - - if groupId then - for i,group in groups do - if group.Id == groupId then - return true + }; + + SetBanMessage = { + Prefix = Settings.Prefix; + Commands = {"setbanmessage", "setbmsg"}; + Args = {"message"}; + Filter = true; + Description = "Sets the ban message banned players see"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + Variables.BanMessage = assert(args[1], "Missing message (argument #1)") + end + }; + + SetLockMessage = { + Prefix = Settings.Prefix; + Commands = {"setlockmessage", "slockmsg", "setlmsg"}; + Args = {"message"}; + Filter = true; + Description = "Sets the lock message unwhitelisted players see if :whitelist or :slock is on"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + Variables.LockMessage = assert(args[1], "Missing message (argument #1)") + end + }; + + SystemMessage = { + Prefix = Settings.Prefix; + Commands = {"sm", "systemmessage", "sysmsg"}; + Args = {"message"}; + Filter = true; + Description = "Same as message but says SYSTEM MESSAGE instead of your name, or whatever system message title is server to..."; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + Functions.Message(Settings.SystemTitle, service.BroadcastFilter(assert(args[1], "Missing message (argument #1)"), plr), service.GetPlayers(), true) + end + }; + + SetCoreGuiEnabled = { + Prefix = Settings.Prefix; + Commands = {"setcoreguienabled", "setcoreenabled", "showcoregui", "setcoregui", "setcgui", "setcore", "setcge"}; + Args = {"player", "All/Backpack/Chat/EmotesMenu/Health/PlayerList", "true/false"}; + Description = "Enables or disables CoreGui elements for the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[3], "Missing state (argument #3)") + local enable = if args[3]:lower() == "on" or args[3]:lower() == "true" then true elseif args[3]:lower() == "off" or args[3]:lower() == "false" then false else nil + assert(enable ~= nil, `Invalid state '{args[3]}'; please supply 'true' or 'false' (argument #3)`) + for _,v in service.GetPlayers(plr, args[1]) do + if string.lower(args[3]) == "on" or string.lower(args[3]) == "true" then + Remote.Send(v, "Function", "SetCoreGuiEnabled", args[2], true) + elseif string.lower(args[3]) == 'off' or string.lower(args[3]) == "false" then + Remote.Send(v, "Function", "SetCoreGuiEnabled", args[2], false) end end end - - return false - end, - - IsLax = function(str) - for _, v in {"plr", "user", "player", "brickcolor"} do - if string.match(string.lower(str), v) then - return true - end - end - - return false - end, - - IsMuted = function(player) - local DoCheck = Admin.DoCheck - for _, v in Settings.Muted do - if DoCheck(player, v) then - return true - end - end - - for _, v in HTTP.Trello.Mutes do - if DoCheck(player, v) then - return true + }; + + Alert = { + Prefix = Settings.Prefix; + Commands = {"alert", "alarm", "annoy"}; + Args = {"player", "message"}; + Filter = true; + Description = "Get someone's attention"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr,string.lower(args[1]))do + Remote.MakeGui(v, "Alert", {Message = args[2] and service.Filter(args[2],plr, v) or "Wake up; Your attention is required"}) + end + end + }; + + LockMap = { + Prefix = Settings.Prefix; + Commands = {"lockmap"}; + Args = {}; + Description = "Locks the map"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, obj in workspace:GetDescendants()do + if obj:IsA("BasePart")then + obj.Locked = true + end end end + }; - if HTTP.WebPanel.Mutes then - for _, v in HTTP.WebPanel.Mutes do - if DoCheck(player, v) then - return true + UnlockMap = { + Prefix = Settings.Prefix; + Commands = {"unlockmap"}; + Args = {}; + Description = "Unlocks the map"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, obj in workspace:GetDescendants()do + if obj:IsA("BasePart")then + obj.Locked = false end end end - end; - - DoCheck = function(pObj, check, banCheck) - local pType = typeof(pObj) - local cType = typeof(check) - - local pUnWrapped = service.UnWrap(pObj) - - local plr: Player = if pType == "userdata" and pObj:IsA("Player") then pObj - elseif pType == "number" then service.Players:GetPlayerByUserId(pObj) - elseif pType == "string" then service.Players:FindFirstChild(pObj) - elseif typeof(pUnWrapped) == "Instance" and pUnWrapped:IsA("Player") then pUnWrapped - elseif pType == "userdata" then service.Players:GetPlayerByUserId(pObj.UserId) - else nil - if not plr then - return false - end - - if cType == "number" then - return plr.UserId == check - elseif cType == "string" then - if plr.Name == check then - return true - end - - local filterName, filterData = string.match(check, "^(.-):(.+)$") - if filterName then - filterName = string.lower(filterName) - else - return false - end - if filterName == "group" then - local groupId = tonumber((string.match(filterData, "^%d+"))) - if groupId then - local plrRank = Admin.GetGroupLevel(plr.UserId, groupId) - local requiredRank,noRank = tonumber((string.match(filterData, "^%d+:(.+)$"))), string.match(filterData,"^%d+$") - if requiredRank then - if requiredRank < 0 then - return plrRank >= math.abs(requiredRank) - else - return plrRank == requiredRank - end - elseif noRank then - return plrRank > 0 + }; + + BuildingTools = { + Prefix = Settings.Prefix; + Commands = {"btools", "f3x", "buildtools", "buildingtools", "buildertools"}; + Args = {"player"}; + Description = "Gives the target player(s) F3X building tools."; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local F3X = Deps.Assets:FindFirstChild("F3X Deps") and (function(deps) + local F3X = service.New("Tool", { + GripPos = Vector3.new(0, 0, 0.4), + CanBeDropped = false, + ManualActivationOnly = false, + ToolTip = "Building Tools by F3X", + Name = "Building Tools" + }, true) + local clonedDeps = deps:Clone() + + for _, obj in clonedDeps:GetDescendants() do + if obj:IsA("BaseScript") then + obj.Disabled = false end end - return false - elseif filterName == "item" then - local itemId = tonumber((string.match(filterData, "^%d+"))) - return itemId and service.CheckAssetOwnership(plr, itemId) - elseif filterName == "gamepass" then - local gamepassId = tonumber((string.match(filterData, "^%d+"))) - return gamepassId and service.CheckPassOwnership(plr, gamepassId) - elseif filterName == "subscription" then - local subscriptionId = string.match(filterData, "^EXP%-%d+$") - return subscriptionId and service.CheckSubscriptionStatus(plr, subscriptionId) - else - local username, userId = string.match(check, "^(.*):(.*)") - if username and userId and (plr.UserId == tonumber(userId) or string.lower(plr.Name) == string.lower(username)) then - return true + for _, obj in clonedDeps:GetChildren() do + obj.Parent = F3X end - if not banCheck and type(check) == "string" and not string.find(check, ":") then - local cache = Functions.GetUserIdFromNameAsync(check) - if cache and plr.UserId == cache then - return true - end - end - end - elseif cType == "table" then - local groupId, rank = check.Group, check.Rank - if groupId and rank then - local plrGroupInfo = Admin.GetPlayerGroup(plr, groupId) - if plrGroupInfo then - local plrRank = plrGroupInfo.Rank - return plrRank == rank or (rank < 0 and plrRank >= math.abs(rank)) - end - end - end + clonedDeps:Destroy() + return F3X + end)(Deps.Assets:FindFirstChild("F3X Deps")) or Variables.F3XCached and Variables.F3XCached:Clone() or require(580330877)() + Variables.F3XCached = Variables.F3XCached or F3X:Clone() + service.New("StringValue", { + Name = `__ADONIS_VARIABLES_{Variables.CodeName}`, + Parent = F3X + }) - return check == plr - end; + for _, v in service.GetPlayers(plr, args[1]) do + local Backpack = v:FindFirstChildOfClass("Backpack") - LevelToList = function(lvl) - local lvl = tonumber(lvl) - if not lvl then return nil end - local listName = Admin.LevelToListName(lvl) - if listName then - local list = Settings.Ranks[listName]; - if list then - return list.Users, listName, list; + if Backpack then + F3X:Clone().Parent = Backpack + end end - end - end; - LevelToListName = function(lvl) - if lvl > 999 then - return "Place Owner" - elseif lvl == 0 then - return "Players" + F3X:Destroy() end + }; - --// Check if this is a default rank and if the level matches the default (so stuff like [Trello] Admins doesn't appear in the command list) - for i,v in server.Defaults.Settings.Ranks do - local tRank = Settings.Ranks[i]; - if tRank and tRank.Level == v.Level and v.Level == lvl then - return i - end - end + Insert = { + Prefix = Settings.Prefix; + Commands = {"insert", "ins"}; + Args = {"id"}; + Description = "Inserts whatever object belongs to the ID you supply, the object must be in the place owner's or Roblox's inventory"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local id = string.lower(args[1]) - for i,v in Settings.Ranks do - if v.Level == lvl then - return i - end - end - end; - - UpdateCachedLevel = function(p, data) - local data = data or Core.GetPlayer(p) - local oLevel, oRank = data.AdminLevel, data.AdminRank - local level, rank = Admin.GetUpdatedLevel(p, data) - - data.AdminLevel = level - data.AdminRank = rank - data.LastLevelUpdate = os.time() - - AddLog("Script", { - Text = `Updating cached level for {p.Name}`; - Desc = `Updating the cached admin level for {p.Name}`; - Player = p; - }) - - if Settings.Console and (oLevel ~= level or oRank ~= rank) then - if not Settings.Console_AdminsOnly or (Settings.Console_AdminsOnly and level > 0) then - task.defer(Remote.RefreshGui, p, "Console") - else - task.defer(Remote.RemoveGui, p, "Console") + for i, v in Variables.InsertList do + if id == string.lower(v.Name)then + id = v.ID + break + end end - end - - return level, rank - end; - - GetLevel = function(p) - local data = Core.GetPlayer(p) - local level = data.AdminLevel - local rank = data.AdminRank - local lastUpdate = data.LastLevelUpdate or 0 - local clients = Remote.Clients - local key = tostring(p.UserId) - - local currentTime = os.time() - if (not level or not lastUpdate or currentTime - lastUpdate > Admin.AdminLevelCacheTimeout) or lastUpdate > currentTime then - local newLevel, newRank = Admin.UpdateCachedLevel(p, data) - - if clients[key] and level and newLevel and type(p) == "userdata" and p:IsA("Player") then - if newLevel < level then - Functions.Hint(`Your admin level has been reduced to {newLevel} [{newRank or "Unknown"}]`, {p}) - elseif newLevel > level then - Functions.Hint(`Your admin level has been increased to {newLevel} [{newRank or "Unknown"}]`, {p}) + for i, v in HTTP.Trello.InsertList do + if id == string.lower(v.Name) then + id = v.ID + break end end - return newLevel, newRank - end - - return level or 0, rank; - end; - - GetUpdatedLevel = function(p, data) - local checkTable = Admin.CheckTable - local doCheck = Admin.DoCheck - - for _, admin in Admin.SpecialLevels do - if doCheck(p, admin.Player) then - return admin.Level, admin.Rank + local obj = service.Insert(tonumber(id), true) + if obj and plr.Character then + table.insert(Variables.InsertedObjects, obj) + obj.Parent = workspace + pcall(obj.MakeJoints, obj) + obj:PivotTo(plr.Character:GetPivot()) + end + end + }; + + SaveTool = { + Prefix = Settings.Prefix; + Commands = {"addtool", "savetool", "maketool"}; + Args = {"optional player", "optional new tool name"}; + Description = `Saves the equipped tool to the storage so that it can be inserted using {Settings.Prefix}give`; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + local tool = v.Character and v.Character:FindFirstChildWhichIsA("BackpackItem") + if tool then + tool = tool:Clone() + if args[2] then tool.Name = args[2] end + tool.Parent = service.UnWrap(Settings.Storage) + Variables.SavedTools[tool] = service.FormatPlayer(plr) + Functions.Hint(`Added tool: {tool.Name}`, {plr}) + elseif not args[1] then + error("You must have an equipped tool to add to the storage.") + end end end - - local sortedRanks = {} - for rank, data in Settings.Ranks do - table.insert(sortedRanks, { - Rank = rank; - Users = data.Users; - Level = data.Level; - }); - end - - table.sort(sortedRanks, function(t1, t2) - return t1.Level > t2.Level - end) - - local highestLevel = 0 - local highestRank = nil - - for _, data in sortedRanks do - local level = data.Level - if level > highestLevel then - for _, v in data.Users do - if doCheck(p, v) then - highestLevel, highestRank = level, data.Rank - break + }; + + ClearSavedTools = { + Prefix = Settings.Prefix; + Commands = {"clraddedtools", "clearaddedtools", "clearsavedtools", "clrsavedtools"}; + Args = {}; + Description = `Removes any tools in the storage added using {Settings.Prefix}savetool`; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local count = 0 + for tool in Variables.SavedTools do + count += 1 + tool:Destroy() + end + table.clear(Variables.SavedTools) + Functions.Hint(string.format("Cleared %d saved tool%s.", count, count == 1 and "" or "s"), {plr}) + end + }; + + NewTeam = { + Prefix = Settings.Prefix; + Commands = {"newteam", "createteam", "maketeam"}; + Args = {"name", "BrickColor"}; + Filter = true; + Description = "Make a new team with the specified name and color"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local teamName = assert(args[1], "Missing team name (argument #1)") + local teamColor = Functions.ParseBrickColor(args[2]) + + if service.Teams:FindFirstChild(teamName) then + Functions.Hint(string.format("Team '%s' already exists!", teamName), {plr}) + return; + end + + service.New("Team", { + Parent = service.Teams; + Name = teamName; + TeamColor = teamColor; + AutoAssignable = false; + }) + if Settings.CommandFeedback then + Functions.Hint(string.format("Created new team '%s' (%s)", teamName, teamColor.Name), {plr}) + end + end + }; + + RemoveTeam = { + Prefix = Settings.Prefix; + Commands = {"removeteam", "deleteteam"}; + Args = {"name"}; + Description = "Remove the specified team"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.Teams:GetTeams() do + if string.sub(string.lower(v.Name), 1, #args[1]) == string.lower(args[1]) then + local ans = Remote.GetGui(plr, "YesNoPrompt", { Question = `Remove team: '{v.Name}'?` }) + + if ans == "Yes" then + v:Destroy() + return Functions.Hint(`Removed team {v.Name}`, {plr}) + else + return Functions.Hint("Cancelled team removal operation", {plr}) end end end end + }; - if Admin.IsPlaceOwner(p) and highestLevel < 1000 then - return 1000, "Place Owner" - end - - return highestLevel, highestRank - end; + RestoreMap = { + Prefix = Settings.Prefix; + Commands = {"restoremap", "maprestore", "rmap"}; + Args = {}; + Description = "Restore the map to the the way it was the last time it was backed up"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local plrName = plr and service.FormatPlayer(plr) or "" - IsPlaceOwner = function(p) - if type(p) == "userdata" and p:IsA("Player") then - --// These are my accounts; Lately I've been using my game dev account(698712377) more so I'm adding it so I can debug without having to sign out and back in (it's really a pain) - --// Disable CreatorPowers in settings if you don't trust me. It's not like I lose or gain anything either way. Just re-enable it BEFORE telling me there's an issue with the script so I can go to your place and test it. - if Settings.CreatorPowers and table.find(Variables.DeveloperWhitelist, p.UserId) then - return true + if not Variables.MapBackup then + error("Cannot restore when there are no backup maps!", 0) + return end - - if tonumber(CreatorId) and p.UserId == CreatorId then - return true + if Variables.RestoringMap then + error("Map has not been backed up",0) + return end - - if p.UserId == -1 and Variables.IsStudio then --// To account for player emulators in multi-client Studio tests - return true + if Variables.BackingupMap then + error("Cannot restore map while backing up map is in process!", 0) + return end - end - end; - - CheckAdmin = function(p) - return Admin.GetLevel(p) > 0 - end; - SetLevel = function(p, level, doSave, rankName) - local current, rank = Admin.GetLevel(p) + Variables.RestoringMap = true + Functions.Hint("Restoring Map...", service.Players:GetPlayers()) + workspace.Gravity = Variables.OriginalGravity - if tonumber(level) then - if current >= 1000 then - return false - else - Admin.SpecialLevels[tostring(p.UserId)] = { - Player = p.UserId, - Level = level, - Rank = rankName - } + for _, obj in workspace:GetChildren() do + if obj.ClassName ~= "Terrain" and not service.Players:GetPlayerFromCharacter(obj) then + obj:Destroy() + service.RunService.Stepped:Wait() + end end - elseif level == "Reset" then - Admin.SpecialLevels[tostring(p.UserId)] = nil - end - Admin.UpdateCachedLevel(p) - end; - - IsTempAdmin = function(p) - local DoCheck = Admin.DoCheck - for i,v in Admin.TempAdmins do - if DoCheck(p,v) then - return true, i + local new = Variables.MapBackup:Clone() + for _, obj in new:GetChildren() do + obj.Parent = workspace + if obj:IsA("Model") then + obj:MakeJoints() + end end - end - end; + new:Destroy() - RemoveAdmin = function(p, temp, override) - local current, rank = Admin.GetLevel(p) - local listData = rank and Settings.Ranks[rank] - local listName = listData and rank - local list = listData and listData.Users - - local isTemp,tempInd = Admin.IsTempAdmin(p) - - if isTemp then - temp = true - table.remove(Admin.TempAdmins, tempInd) - end - - if override then - temp = false - end - - if type(p) == "userdata" then - Admin.SetLevel(p, 0) - end - - if list then - local DoCheck = Admin.DoCheck - for ind,check in list do - if DoCheck(p, check) and not (type(check) == "string" and (string.match(check,"^Group:") or string.match(check,"^Item:"))) then - table.remove(list, ind) - - if not temp and Settings.SaveAdmins then - TrackTask("Thread: RemoveAdmin", Core.DoSave, false, { - Type = "TableRemove"; - Table = {"Settings", "Ranks", listName, "Users"}; - Value = check; - }) - end - end + local Terrain = workspace.Terrain or workspace:FindFirstChildOfClass("Terrain") + if Terrain and Variables.TerrainMapBackup then + Terrain:Clear() + Terrain:PasteRegion(Variables.TerrainMapBackup, Terrain.MaxExtents.Min, true) end - end - Admin.UpdateCachedLevel(p) - end; - - AddAdmin = function(p, level, temp) - local current, rank = Admin.GetLevel(p) - local list = rank and Settings.Ranks[rank] - local levelName, newRank, newList - - if type(level) == "string" then - local newRank = Settings.Ranks[level] - levelName = newRank and level - newList = newRank and newRank.Users - level = (newRank and newRank.Level) or Admin.StringToComLevel(levelName) or level - else - local nL, nLN = Admin.LevelToList(level) - levelName = nLN - newRank = nLN - newList = nL - end + task.wait() - Admin.RemoveAdmin(p, temp) - Admin.SetLevel(p, level, nil, levelName) + Admin.RunCommand(`{Settings.Prefix}fixlighting`) + Admin.RunCommand(`{Settings.Prefix}respawn`, "all") + Variables.RestoringMap = false + Functions.Hint('Map Restore Complete.',service.Players:GetPlayers()) - if temp then - table.insert(Admin.TempAdmins, p) + Logs:AddLog("Script", { + Text = "Map Restoration Complete", + Desc = `{plrName} has restored the map.`, + }) end - - if list and type(list) == "table" then - local index,value - - for ind,ent in list do - if (type(ent)=="number" or type(ent)=="string") and (ent==p.UserId or string.lower(ent)==string.lower(p.Name) or string.lower(ent)==string.lower(`{p.Name}:{p.UserId}`)) then - index = ind - value = ent - end - end - - if index and value then - table.remove(list, index) + }; + + ScriptBuilder = { + Prefix = Settings.Prefix; + Commands = {"scriptbuilder", "scriptb", "sb"}; + Args = {"create/remove/edit/close/clear/append/run/stop/list", "localscript/script", "scriptName", "data"}; + Description = "[Deprecated] Script Builder; make a script, then edit it and chat it's code or use :sb append "; + AdminLevel = "Admins"; + Hidden = true; + NoFilter = true; + CrossServerDenied = true; + Function = function(plr: Player, args: {string}) + assert(Settings.CodeExecution, "CodeExecution must be enabled for this command to work") + local sb = Variables.ScriptBuilder[tostring(plr.UserId)] + if not sb then + sb = { + Script = {}; + LocalScript = {}; + Events = {}; + } + Variables.ScriptBuilder[tostring(plr.UserId)] = sb end - end - local value = `{p.Name}:{p.UserId}` + local action = string.lower(args[1]) + local class = args[2] or "LocalScript" + local name = args[3] - if newList then - table.insert(newList, value) - - if Settings.SaveAdmins and levelName and not temp then - TrackTask("Thread: SaveAdmin", Core.DoSave, false, { - Type = "TableAdd"; - Table = {"Settings", "Ranks", levelName, "Users"}; - Value = value - }) + if string.lower(class) == "script" or string.lower(class) == "s" then + class = "Script" + elseif string.lower(class) == "clientscript" or string.lower(class) == "cs" then + class = "ClientScript" + --elseif string.lower(class) == "localscript" or string.lower(class) == "ls" then + -- class = "LocalScript" + else + class = "LocalScript" end - end - Admin.UpdateCachedLevel(p) - end; - - CheckDonor = function(p) - local key = tostring(p.UserId) - if Variables.CachedDonors[key] then - return true - else - local pGroup = Admin.GetPlayerGroup(p, 886423) - for _, pass in Variables.DonorPass do - if p.Parent ~= service.Players then - return false - end - - local ran, ret - if type(pass) == "number" then - ran, ret = pcall(service.MarketPlace.UserOwnsGamePassAsync, service.MarketPlace, p.UserId, pass) - elseif type(pass) == "string" and tonumber(pass) then - ran, ret = pcall(service.MarketPlace.PlayerOwnsAsset, service.MarketPlace, p, tonumber(pass)) - end + if action == "create" then + assert(args[1] and args[2] and args[3], "Missing arguments") + local code = args[4] or " " - if (ran and ret) or (pGroup and pGroup.Rank >= 10) then --// Complimentary donor access is given to Adonis contributors & developers. - Variables.CachedDonors[key] = os.time() - return true + if sb[class][name] then + pcall(function() + sb[class][name].Script.Disabled = true + sb[class][name].Script:Destroy() + end) + if sb.ChatEvent then + sb.ChatEvent:Disconnect() + end end - end - end - end; - CheckBan = function(p) - local doCheck = Admin.DoCheck - local banCheck = Admin.DoBanCheck + local wrapped,scr = Core.NewScript(class,code,false,true) - for ind, admin in Settings.Banned do - if (type(admin) == "table" and ((admin.UserId and doCheck(p, admin.UserId, true)) or (admin.Name and not admin.UserId and doCheck(p, admin.Name, true)))) or doCheck(p, admin, true) then - return true, (type(admin) == "table" and admin.Reason) - end - end + sb[class][name] = { + Wrapped = wrapped; + Script = scr; + } - for ind, ban in Core.Variables.TimeBans do - if p.UserId == ban.UserId then - if ban.EndTime-os.time() <= 0 then - table.remove(Core.Variables.TimeBans, ind) + if args[4] then + Functions.Hint(`Created {class} {name} and appended text`, {plr}) else - return true, `\n {ban.Reason or "(No reason provided.)"}\n | Banned until {service.FormatTime(ban.EndTime, {WithWrittenDate = true})}` + Functions.Hint(`Created {class} {name}`, {plr}) end - end - end - - for ind, admin in HTTP.Trello.Bans do - local name = type(admin) == "table" and admin.Name or admin - if doCheck(p, name) or banCheck(p, name) then - return true, (type(admin) == "table" and admin.Reason and service.Filter(admin.Reason, p, p)) - end - end - - if HTTP.WebPanel.Bans then - for ind, admin in HTTP.WebPanel.Bans do - if doCheck(p, admin) or banCheck(p, admin) then - return true, (type(admin) == "table" and admin.Reason) + elseif action == "edit" then + assert(args[1] and args[2] and args[3], "Missing arguments") + if sb[class][name] then + local scr = sb[class][name].Script + local tab = Core.GetScript(scr) + if scr and tab then + sb[class][name].Event = plr.Chatted:Connect(function(msg) + if string.sub(msg, 1,#(`{Settings.Prefix}sb`)) ~= `{Settings.Prefix}sb` then + tab.Source ..= `\n{msg}` + Functions.Hint(`Appended message to {class} {name}`, {plr}) + end + end) + Functions.Hint(`Now editing {class} {name}; Chats will be appended`, {plr}) + end + else + error(`{class} {name} not found!`) end - end - end - end; - - AddBan = function(p, reason, doSave, moderator, banType) - local value = { - Name = p.Name; - UserId = p.UserId; - Reason = reason; - Moderator = if moderator then service.FormatPlayer(moderator) else "%SYSTEM%"; - BanType = banType - } - - table.insert(Settings.Banned, value) - - if doSave then - Core.DoSave({ - Type = "TableAdd"; - Table = "Banned"; - Value = value; - }) - - Core.CrossServer("RemovePlayer", p.Name, Variables.BanMessage, value.Reason or "No reason provided") - end - - if type(p) ~= "table" then - if not service.Players:FindFirstChild(p.Name) then - Remote.Send(p,'Function','KillClient') - else - if p then pcall(function() p:Kick(`{Variables.BanMessage} | Reason: {value.Reason or "No reason provided"}`) end) end - end - end - - service.Events.PlayerBanned:Fire(p, reason, doSave, moderator) - end; - - AddTimeBan = function(p : Player | {[string]: any}, duration: number, reason: string, moderator: Player?) - local value = { - Name = p.Name; - UserId = p.UserId; - EndTime = os.time() + tonumber(duration); - Reason = reason; - Moderator = if moderator then service.FormatPlayer(moderator) else "%SYSTEM%"; - } - - table.insert(Core.Variables.TimeBans, value) - - Core.DoSave({ - Type = "TableAdd"; - Table = {"Core", "Variables", "TimeBans"}; - Value = value; - }) - - Core.CrossServer("RemovePlayer", p.Name, Variables.BanMessage, value.Reason or "No reason provided") - - if type(p) ~= "table" then - if not service.Players:FindFirstChild(p.Name) then - Remote.Send(p, "Function", "KillClient") - else - if p then pcall(function() p:Kick(`{Variables.BanMessage} | Reason: {value.Reason or "No reason provided"}`) end) end - end - end - - service.Events.PlayerBanned:Fire(p, reason, true, moderator) - end, - - DoBanCheck = function(name: string | number | Instance, check: string | {[string]: any}) - local id = type(name) == "number" and name - - if type(name) == "userdata" and name:IsA("Player") then - id = name.UserId - name = name.Name - end - - if type(check) == "table" then - if type(name) == "string" and check.Name and string.lower(check.Name) == string.lower(name) then - return true - elseif id and check.UserId and check.UserId == id then - return true - end - elseif type(check) == "string" then - local cName, cId = string.match(check, "(.*):(.*)") - if not cName and cId then cName = check end - - if cName then - if string.lower(cName) == string.lower(name) then - return true - elseif id and cId and id == tonumber(cId) then - return true + elseif action == "close" then + assert(args[1] and args[2] and args[3], "Missing arguments") + local scr = sb[class][name].Script + local tab = Core.GetScript(scr) + if sb[class][name] then + if sb[class][name].Event then + sb[class][name].Event:Disconnect() + sb[class][name].Event = nil + Functions.Hint(`No longer editing {class} {name}`, {plr}) + end + else + error(`{class} {name} not found!`) end - else - return string.lower(tostring(check)) == string.lower(tostring(name)) - end - end - - return false - end; - - RemoveBan = function(name, doSave) - local ret - for i,v in Settings.Banned do - if Admin.DoBanCheck(name, v) then - ret = table.remove(Settings.Banned, i) - if doSave then - Core.DoSave({ - Type = "TableRemove"; - Table = "Banned"; - Value = ret; - LaxCheck = true; - }) + elseif action == "clear" then + assert(args[1] and args[2] and args[3], "Missing arguments") + local scr = sb[class][name].Script + local tab = Core.GetScript(scr) + if scr and tab then + tab.Source = " " + Functions.Hint(`Cleared {class} {name}`, {plr}) + else + error(`{class} {name} not found!`) end - end - end - return ret - end; - - RemoveTimeBan = function(name : string | number | Instance) - local ret - for i,v in Core.Variables.TimeBans do - if Admin.DoBanCheck(name, v) then - table.remove(Core.Variables.TimeBans, i) - ret = v - Core.DoSave({ - Type = "TableRemove"; - Table = {"Core", "Variables", "TimeBans"}; - Value = v; - LaxCheck = true; - }) - end - end - return ret - end, - - RunCommand = function(coma: string, ...) - local _, com = Admin.GetCommand(coma) - if com then - local cmdArgs = com.Args or com.Arguments - local args = Admin.GetArgs(coma, #cmdArgs, ...) - - TrackTask(`Command: {coma}`, com.Function, function(err) - warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`) - end, false, args) - end - end; - - RunCommandAsPlayer = function(coma, plr, ...) - local ind, com = Admin.GetCommand(coma) - if com then - local adminLvl = Admin.GetLevel(plr) - - local cmdArgs = com.Args or com.Arguments - local args = Admin.GetArgs(coma, #cmdArgs, ...) - local ran, error = TrackTask( - `{plr.Name}: {coma}`, - com.Function, - function(err) - err = string.match(err, ":(.+)$") or "Unknown error" - Remote.MakeGui(plr, "Output", { - Title = "Error", - Message = error, - Color = Color3.new(1, 0, 0), - }) - warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`) - end, - plr, - args, - { - PlayerData = { - Player = plr, - Level = adminLvl, - isDonor = ((Settings.DonorCommands or com.AllowDonors) and Admin.CheckDonor(plr)) or false, - }, - } - ) - end - end; - - RunCommandAsNonAdmin = function(coma, plr, ...) - local ind, com = Admin.GetCommand(coma) - if com and com.AdminLevel == 0 then - local cmdArgs = com.Args or com.Arguments - local args = Admin.GetArgs(coma, #cmdArgs, ...) - local _, error = TrackTask( - `{plr.Name}: {coma}`, - com.Function, - function(err) - err = string.match(err, ":(.+)$") or "Unknown error" - Remote.MakeGui(plr, "Output", { - Title = "", - Message = error, - Color = Color3.new(1, 0, 0), - }) - warn(`Encountered an error while running a command: {coma}\n{err}\n{debug.traceback()}`) - end, - plr, - args, - { PlayerData = { - Player = plr, - Level = 0, - isDonor = false, - } } - ) - end - end; - - CacheCommands = function() - local tempTable = {} - local tempPrefix = {} - for ind, data in Commands do - if type(data) == "table" then - for i,cmd in data.Commands do - if type(data.Prefix) ~= "table" and data.Prefix == "" then Admin.BlankPrefix = true end - if type(data.Prefix) == "table" then - for _,p in data.Prefix do - tempPrefix[p] = true - tempTable[string.lower(p..cmd)] = ind - end - else - tempPrefix[data.Prefix] = true - tempTable[string.lower(data.Prefix..cmd)] = ind + elseif action == "remove" then + assert(args[1] and args[2] and args[3], "Missing arguments") + if sb[class][name] then + pcall(function() + sb[class][name].Script.Disabled = true + sb[class][name].Script:Destroy() + end) + if sb.ChatEvent then + sb.ChatEvent:Disconnect() + sb.ChatEvent = nil end + sb[class][name] = nil + else + error(`{class} {name} not found!`) end - end - end - - Admin.PrefixCache = tempPrefix - Admin.CommandCache = tempTable - - if Variables.ChatCreateRobloxCommands then - -- // Support for commands to be ran via TextChat - task.spawn(function() - local container = service.TextChatService.ChatVersion == Enum.ChatVersion.TextChatService and service.TextChatService:WaitForChild("TextChatCommands", 9e9) - - if container then - for _, v in container:GetChildren() do - if string.sub(v.Name, 1, 7) == "Adonis_" then - v:Destroy() - end + elseif action == "append" then + assert(args[1] and args[2] and args[3] and args[4], "Missing arguments") + if sb[class][name] then + local scr = sb[class][name].Script + local tab = Core.GetScript(scr) + if scr and tab then + tab.Source ..= `\n{args[4]}` + Functions.Hint(`Appended message to {class} {name}`, {plr}) end - - local blacklistedCommands = {} - - for _, v in container:GetDescendants() do - if v:IsA("TextChatCommand") then - blacklistedCommands[v.PrimaryAlias] = true - blacklistedCommands[v.SecondaryAlias] = true - end + else + error(`{class} {name} not found!`) + end + elseif action == "run" then + assert(args[1] and args[2] and args[3], "Missing arguments") + if sb[class][name] then + if class == "LocalScript" then + sb[class][name].Script.Parent = plr:FindFirstChildOfClass("Backpack") + else + sb[class][name].Script.Parent = service.ServerScriptService end + sb[class][name].Script.Disabled = true + task.wait(0.03) + sb[class][name].Script.Disabled = false + Functions.Hint(`Running {class} {name}`, {plr}) + else + error(`{class} {name} not found!`) + end + elseif action == "stop" then + assert(args[1] and args[2] and args[3], "Missing arguments") + if sb[class][name] then + sb[class][name].Script.Disabled = true + Functions.Hint(`Stopped {class} {name}`, {plr}) + else + error(`{class} {name} not found!`) + end + elseif action == "list" then + local tab = {} + for i, v in sb.Script do + table.insert(tab, {Text = `Script: {i}`, Desc = `Running: {v.Script.Disabled}`}) + end - for name, data in Commands do - local command1, command2 = nil, nil + for i, v in sb.LocalScript do + table.insert(tab, {Text = `LocalScript: {i}`, Desc = `Running: {v.Script.Disabled}`}) + end - if type(data) ~= "table" or data.Hidden then - continue - end + Remote.MakeGui(plr, "List", {Title = "SB Scripts", Table = tab}) + end + end + }; + + MakeScript = { + Prefix = Settings.Prefix; + Commands = {"s", "ss", "serverscript", "sscript", "script", "makescript"}; + Args = {"code"}; + Description = "Executes the given Lua code on the server"; + AdminLevel = "Admins"; + NoFilter = true; + CrossServerDenied = true; + Function = function(plr: Player, args: {string}) + assert(Settings.CodeExecution, "CodeExecution config must be enabled for this command to work") + local bytecode = Core.Bytecode(assert(args[1], "Missing Script code (argument #2)")) + assert(string.find(bytecode, "\27Lua"), `Script unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`) + + local cl = Core.NewScript("Script", args[1], true) + cl.Name = "[Adonis] Script" + cl.Parent = service.ServerScriptService + task.wait() + cl.Disabled = false + Functions.Hint("Ran Script", {plr}) + end + }; + + MakeLocalScript = { + Prefix = Settings.Prefix; + Commands = {"ls", "localscript", "lscript"}; + Args = {"code"}; + Description = "Executes the given code on your client"; + AdminLevel = "Admins"; + NoFilter = true; + Function = function(plr: Player, args: {string}) + Commands.LoadLocalScript.Function(plr, {`@{plr.Name}`, args[1]}) + end + }; + + LoadLocalScript = { + Prefix = Settings.Prefix; + Commands = {"cs", "cscript", "clientscript"}; + Args = {"player", "code"}; + Description = "Executes the given code on the client of the target player(s)"; + AdminLevel = "Admins"; + NoFilter = true; + Function = function(plr: Player, args: {string}) + assert(args[2], "Missing LocalScript code (argument #2)") + + local bytecode = Core.Bytecode(args[2]) + assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`) + + local new = Core.NewScript("LocalScript", `script.Parent = game:GetService('Players').LocalPlayer.PlayerScripts; {args[2]}`, true) + local function cloneScript(targetPlayer) + local playerName = if targetPlayer == plr then "your client" else service.FormatPlayer(targetPlayer) + + local backpack = targetPlayer:FindFirstChildOfClass("Backpack") + if not backpack then + Functions.Hint(`Couldn't run LocalScript on {playerName} (Backpack missing?)`, {plr}) + return + end - for _, v in data.Commands do - if type(data.Prefix) == "table" then - for _, p in data.Prefix do - if not blacklistedCommands["/"..p..v] then - if not command1 then - command1 = "/"..p..v - else - command2 = "/"..p..v - end - end - end - else - if not blacklistedCommands["/"..data.Prefix..v] then - if not command1 then - command1 = "/"..data.Prefix..v - else - command2 = "/"..data.Prefix..v - end - end + local cl = new:Clone() + cl.Name = "[Adonis] LocalScript" + cl.Disabled = true + cl.Parent = targetPlayer:FindFirstChildOfClass("Backpack") + task.wait(.1) + cl.Disabled = false + Functions.Hint(`Ran LocalScript on {playerName}`, {plr}) + end + + for i, v in service.GetPlayers(plr, args[1]) do + task.spawn(cloneScript, v) + end + end + }; + + CreateStarterScript = { + Prefix = Settings.Prefix; + Commands = {"starterscript", "clientstarterscript", "starterclientscript", "createstarterscript"}; + Args = {"name", "code"}; + Description = "Executes the given code on everyone's client upon respawn"; + AdminLevel = "Admins"; + NoFilter = true; + Function = function(plr: Player, args: {string}) + assert(args[1], "Missing starter script name (argument #1)") + assert(args[2], "Missing LocalScript code (argument #2)") + + local bytecode = Core.Bytecode(args[2]) + assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`) + + local new = Core.NewScript("LocalScript", args[2], true) + new.Name = `[Adonis] {args[1]}` + new.Parent = service.StarterGui + new.Disabled = false + Functions.Hint("Created starter script", {plr}) + end + }; + + + StarterScripts = { + Prefix = Settings.Prefix; + Commands = {"starterscripts", "clientstarterscripts", "starterclientscripts"}; + Args = {}; + Description = "Show existing starterscripts"; + AdminLevel = "Admins"; + NoFilter = true; + Function = function(plr: Player, args: {string}) + local result = {} + + for _,v : Instance in service.StarterGui:GetChildren() do + if v:IsA("LocalScript") and v.Name:find("[Adonis]") then + table.insert(result, (v.Name:gsub("%[Adonis%] ", ""))) + end + end - end - end + Remote.MakeGui(plr,"List",{ + Title = "Starter Scripts"; + Tab = result; + }) + end + }; - if command1 then - local command = service.New("TextChatCommand") - command.Name = "Adonis_"..name - command.PrimaryAlias = command1 - command.SecondaryAlias = command2 or "" - command.Archivable = false - command:SetAttribute("AdminLevel", tonumber(data.AdminLevel) or data.AdminLevel or nil) - command.Parent = container - command.Triggered:Connect(function(textSource, text) - local player = service.Players:GetPlayerByUserId(textSource.UserId) + RemoveStarterScript = { + Prefix = Settings.Prefix; + Commands = {"removestarterscript", "removeclientstarterscripts", "removestarterclientscripts", "unstarterscript"}; + Args = {"name"}; + Description = "Remove a starterscript"; + AdminLevel = "Admins"; + NoFilter = true; + Function = function(plr: Player, args: {string}) + assert(args[1], "No starterscript name provided!") - if player then - Process.Command(player, string.sub(text, 2)) - end - end) - end + for _,v : Instance in service.StarterGui:GetChildren() do + if v:IsA("LocalScript") and v.Name:find("[Adonis]") then + if v.Name:gsub("%[Adonis%] ", ""):lower() == args[1]:lower() or args[1]:lower() == "all" then + service.Delete(v) + Functions.Hint("Removed starter script "..v.Name, {plr}) end end - end) + end end - end; - - GetCommand = function(Command) - if Admin.PrefixCache[string.sub(Command, 1, 1)] or Admin.BlankPrefix then - local matched - matched = if string.find(Command, Settings.SplitKey) then - string.match(Command, `^(%S+){Settings.SplitKey}`) - else string.match(Command, "^(%S+)") - - if matched then - local found = Admin.CommandCache[string.lower(matched)] - if found then - local real = Commands[found] - if real then - return found,real,matched + }; + + Note = { + Prefix = Settings.Prefix; + Commands = {"note", "writenote", "makenote"}; + Args = {"player", "note"}; + Filter = true; + Description = "Makes a note on the target player(s) that says "; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[2], "Missing note (argument #2)") + for _, v in service.GetPlayers(plr, args[1]) do + local PlayerData = Core.GetPlayer(v) + if not PlayerData.AdminNotes then PlayerData.AdminNotes = {} end + table.insert(PlayerData.AdminNotes, args[2]) + Functions.Hint(`Added {service.FormatPlayer(v)} Note {args[2]}`, {plr}) + Core.SavePlayer(v, PlayerData) + end + end + }; + + DeleteNote = { + Prefix = Settings.Prefix; + Commands = {"removenote", "remnote", "deletenote", "clearnote"}; + Args = {"player", "note (specify 'all' to delete all notes)"}; + Description = "Removes a note on the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[2], "Missing note (argument #2)") + for _, v in service.GetPlayers(plr, args[1]) do + local PlayerData = Core.GetPlayer(v) + if PlayerData.AdminNotes then + if string.lower(args[2]) == "all" then + PlayerData.AdminNotes = {} + else + for k, m in PlayerData.AdminNotes do + if string.sub(string.lower(m), 1, #args[2]) == string.lower(args[2]) then + Functions.Hint(`Removed {service.FormatPlayer(v)} Note {m}`, {plr}) + table.remove(PlayerData.AdminNotes, k) + end + end end + Core.SavePlayer(v, PlayerData) end end end - end; - - FindCommands = function(Command) - local prefixChar = string.sub(Command, 1, 1) - local checkPrefix = Admin.PrefixCache[prefixChar] and prefixChar - local matched - - if checkPrefix then - Command = string.sub(Command, 2) - end - - if string.find(Command, Settings.SplitKey) then - matched = string.match(Command, `^(%S+){Settings.SplitKey}`) - else - matched = string.match(Command, "^(%S+)") - end - - if matched then - local foundCmds = {} - matched = string.lower(matched) - - for ind,cmd in Commands do - if type(cmd) == "table" and ((checkPrefix and prefixChar == cmd.Prefix) or not checkPrefix) then - for _, alias in cmd.Commands do - if string.lower(alias) == matched then - foundCmds[ind] = cmd - break - end - end + }; + + ShowNotes = { + Prefix = Settings.Prefix; + Commands = {"notes", "viewnotes"}; + Args = {"player"}; + Description = "Views notes on the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + local PlayerData = Core.GetPlayer(v) + local notes = PlayerData.AdminNotes + if not notes then + Functions.Hint(`No notes found on {service.FormatPlayer(v)}`, {plr}) + continue end + Remote.MakeGui(plr, "List", {Title = service.FormatPlayer(v), Table = notes}) + end + end + }; + + LoopKill = { + Prefix = Settings.Prefix; + Commands = {"loopkill"}; + Args = {"player", "num (optional)"}; + Description = "Repeatedly kills the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local num = tonumber(args[2]) or 9999 + + for _, v in service.GetPlayers(plr, args[1]) do + service.StopLoop(`{v.UserId}LOOPKILL`) + local count = 0 + Routine(service.StartLoop, `{v.UserId}LOOPKILL`, 3, function() + local hum = v.Character and v.Character:FindFirstChildOfClass("Humanoid") + if hum and hum.Health > 0 then + hum.Health = 0 + count += 1 + end + if count == num then + service.StopLoop(`{v.UserId}LOOPKILL`) + end + end) end - - return foundCmds end - end; + }; - SetPermission = function(comString, newLevel) - local cmds = Admin.FindCommands(comString) - if cmds then - for ind, cmd in cmds do - cmd.AdminLevel = newLevel + UnLoopKill = { + Prefix = Settings.Prefix; + Commands = {"unloopkill"}; + Args = {"player"}; + Description = "Un-Loop Kill"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + service.StopLoop(`{v.UserId}LOOPKILL`) end end - end; + }; - FormatCommandArguments = function(command) - local text = table.create(#command.Args) - for i, arg in command.Args do - text[i] = `<{arg}>` - end - return table.concat(text, Settings.SplitKey) - end; - - FormatCommand = function(command, cmdn) - return table.concat({ - (if type(command.Prefix) == "table" then command.Prefix[1] else command.Prefix or ""), - tostring(command.Commands[cmdn or 1]), - #command.Args > 0 and Settings.SplitKey or "", - #command.Args > 0 and Admin.FormatCommandArguments(command) or "" - }) - end; - - FormatCommandAdminLevel = function(command) - local levels = if type(command.AdminLevel) == "table" - then table.clone(command.AdminLevel) - else {command.AdminLevel} - local permissionDesc = table.create(#levels) - - for i, lvl in levels do - if type(lvl) == "number" then - local list, name, data = Admin.LevelToList(lvl) - permissionDesc[i] = `{name or "No Rank"}; Level {lvl}` - elseif type(lvl) == "string" then - local numLvl = Admin.StringToComLevel(lvl) - permissionDesc[i] = `{lvl}; Level {numLvl or "Unknown"}` - else - permissionDesc[i] = "N/A" + Lag = { + Prefix = Settings.Prefix; + Commands = {"lag", "fpslag"}; + Args = {"player"}; + Description = "Makes the target player(s)'s FPS drop"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers(plr, args[1]) do + if Admin.CheckAuthority(plr, v, "lag") then + Remote.Send(v, "Function", "SetFPS", 5.6) + end end end + }; - return table.concat(permissionDesc, ", ") - end; - - CheckTable = function(p, tab) - local doCheck = Admin.DoCheck - for i,v in tab do - if doCheck(p, v) then - return true + UnLag = { + Prefix = Settings.Prefix; + Commands = {"unlag", "unfpslag"}; + Args = {"player"}; + Description = "Un-Lag"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + Remote.Send(v, "Function", "RestoreFPS") end end - end; - - --// Make it so you can't accidentally overwrite certain existing commands... resulting in being unable to add/edit/remove aliases (and other stuff) - CheckAliasBlacklist = function(alias) - local playerPrefix = Settings.PlayerPrefix; - local prefix = Settings.Prefix - local blacklist = { - [`{playerPrefix}alias`] = true; - [`{playerPrefix}newalias`] = true; - [`{playerPrefix}removealias`] = true; - [`{playerPrefix}client`] = true; - [`{playerPrefix}userpanel`] = true; - [":adonissettings"] = true; - } - --return Admin.CommandCache[alias:lower()] --// Alternatively, we could make it so you can't overwrite ANY existing commands... - return blacklist[alias]; - end; - - GetArgs = function(msg, num, ...) - local newArgs = table.pack(...) - local args = Functions.Split(string.match(msg, `^.-{Settings.SplitKey}(.+)`) or "", Settings.SplitKey, num) or table.create(newArgs.n) - for i = 1, newArgs.n do - table.insert(args, newArgs[i]) - end - return args - end; - - AliasFormat = function(aliases, msg) - local foundPlayerAlias = false --// Check if there's a player-defined alias first then otherwise check settings aliases + }; - local CheckAliasBlacklist, SanitizePattern = Admin.CheckAliasBlacklist, service.SanitizePattern - - if aliases then - for alias, cmd in aliases do - local tAlias = stripArgPlaceholders(alias) - if not Admin.CheckAliasBlacklist(tAlias) then - local escAlias = SanitizePattern(tAlias) - --// Ignore any "empty" aliases, aka aliases that would basically match any command - if string.len(Functions.Trim(escAlias)) == 0 then - continue - end - local trimmedMsg = Functions.Trim(msg) - --// Use Adonis split to better support various characters that string.split can't handle properly - local aliasCharacters = Functions.Split(trimmedMsg, Settings.SplitKey) - --// Matching an alias can result in an infinite loop like running !fire with the alias !f, it will infinitely run the !f alias - --// If you have an alias !f - if escAlias == aliasCharacters[1] or string.match(trimmedMsg, `%s{escAlias}`) then - msg = FormatAliasArgs(alias, cmd, msg) - end + Crash = { + Prefix = Settings.Prefix; + Commands = {"crash"}; + Args = {"player"}; + Description = "Crashes the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if Admin.CheckAuthority(plr, v, "crash") then + Remote.Send(v, "Function", "Crash") end end end + }; - for alias, cmd in Variables.Aliases do - local tAlias = stripArgPlaceholders(alias) - if not CheckAliasBlacklist(tAlias) then - local escAlias = SanitizePattern(tAlias) - if string.match(msg, `^{escAlias}`) or string.match(msg, `%s{escAlias}`) then - msg = FormatAliasArgs(alias, cmd, msg) + HardCrash = { + Prefix = Settings.Prefix; + Commands = {"hardcrash"}; + Args = {"player"}; + Description = "Hard-crashes the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if Admin.CheckAuthority(plr, v, "hard-crash") then + Remote.Send(v, "Function", "HardCrash") end end end + }; - return msg - end; - - StringToComLevel = function(str) - local strType = type(str) - if strType == "string" and string.lower(str) == "players" then - return 0 - end - if strType == "number" then - return str - end - - local lvl = Settings.Ranks[str] - return (lvl and lvl.Level) or tonumber(str) - end; - - CheckComLevel = function(plrAdminLevel, comLevel) - if type(comLevel) == "string" then - comLevel = Admin.StringToComLevel(comLevel) - elseif type(comLevel) == "table" then - for _, level in comLevel do - if Admin.CheckComLevel(plrAdminLevel, level) then - return true + RAMCrash = { + Prefix = Settings.Prefix; + Commands = {"ramcrash", "memcrash"}; + Args = {"player"}; + Description = "RAM-crashes the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if Admin.CheckAuthority(plr, v, "RAM-crash") then + Remote.Send(v, "Function", "RAMCrash") end end - return false end + }; - return type(comLevel) == "number" and plrAdminLevel >= comLevel - end; - - IsBlacklisted = function(p) - local CheckTable = Admin.CheckTable - for _, list in Variables.Blacklist.Lists do - if CheckTable(p, list) then - return true + GPUCrash = { + Prefix = Settings.Prefix; + Commands = {"gpucrash"}; + Args = {"player"}; + Description = "GPU crashes the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if Admin.CheckAuthority(plr, v, "GPU-crash") then + Remote.Send(v, "Function", "GPUCrash") + end end end - end; + }; - CheckPermission = function(pDat, cmd, ignoreCooldown, opts) - opts = opts or {} + CustomKick = { + Prefix = Settings.Prefix; + Commands = {"ckick", "customkick", "customcrash"}; + Args = {"player", "title", "message"}; + Description = "Disconnects (crashes) the target player with a custom Roblox dialog"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[3], "Argument(s) missing or nil") - local adminLevel = pDat.Level - local comLevel = cmd.AdminLevel + local title = service.BroadcastFilter(args[2], plr) + assert(title == args[2], "Title was filtered: "..title) - if cmd.Disabled then - return false, "This command has been disabled." - end - - if Variables.IsStudio and cmd.NoStudio then - return false, "This command cannot be used in Roblox Studio." - end + local msg = service.BroadcastFilter(args[3], plr) + assert(msg == args[3], "Message was filtered: "..msg) - if opts.CrossServer and cmd.CrossServerDenied then -- Ignore when disabled then - return false, "This command may not be run across servers (cross-server-blacklisted)." - end + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if not Admin.CheckAuthority(plr, v, "custom-kick") then + continue + end - if cmd.CrossServer and not Settings.CrossServerCommands then - return false, "This command has been disabled due to CrossServerCommands being disabled" - end + local plrgui = v:FindFirstChildOfClass("PlayerGui") + if not plrgui then + Remote.MakeGui(plr, "Output", { + Message = `Failed to custom-kick {service.FormatPlayer(v)} (PlayerGui not found)`; + }) + continue + end - if Admin.IsPlaceOwner(pDat.Player) or adminLevel >= Settings.Ranks.Creators.Level then - return true, nil - end + local promptGui = Deps.Assets.RobloxPromptGui:Clone() + promptGui.promptOverlay.ErrorPrompt.TitleFrame.ErrorTitle.Text = title + promptGui.promptOverlay.ErrorPrompt.MessageArea.ErrorFrame.ErrorMessage.Text = msg + promptGui.Parent = plrgui - if Admin.IsBlacklisted(pDat.Player) then - return false, "You are blacklisted from running commands." - end + Remote.Send(v, "Function", "CustomKick") + task.delay(5, function() + if v.Parent == service.Players then + -- make sure they're really kicked + v:Kick("Unexpected Error") + end + end) + Functions.Hint(`Custom-kicking {service.FormatPlayer(v)}`, {plr}) + end + end + }; + + Shutdown = { + Prefix = Settings.Prefix; + Commands = {"shutdown"}; + Args = {"reason"}; + Description = "Shuts the server down"; + Filter = true; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + if Core.DataStore then + Core.UpdateData("ShutdownLogs", function(logs) + table.insert(logs, 1, { + User = plr and plr.Name or "[Server]", + Time = os.time(), + Reason = args[1] or "N/A" + }) - if (comLevel == 0 or comLevel == "Players") and adminLevel <= 0 and not Settings.PlayerCommands then - return false, "Player commands are disabled in this game." - end + local nlogs = #logs + if nlogs > Logs.OldCommandLogsLimit then + table.remove(logs, nlogs) + end - if cmd.Fun and not Settings.FunCommands then - return false, "Fun commands are disabled in this game." - end + return logs + end) + end - if opts.Chat and cmd.Chattable == false then - return false, "This command is not permitted as chat message (non-chattable command)." + Functions.Shutdown(args[1]) end + }; - local permAllowed = (cmd.Donors and (pDat.isDonor and (Settings.DonorCommands or cmd.AllowDonors))) or (cmd.Agent and HTTP.Trello.CheckAgent) and HTTP.Trello.CheckAgent(pDat.Player) - or Admin.CheckComLevel(adminLevel, comLevel) - - if permAllowed and not ignoreCooldown and type(pDat.Player) == "userdata" then - local playerCooldown = tonumber(cmd.PlayerCooldown) - local serverCooldown = tonumber(cmd.ServerCooldown) - local crossCooldown = tonumber(cmd.CrossCooldown) - - local cmdFullName = cmd._fullName or (function() - local aliases = cmd.Aliases or cmd.Commands or {} - cmd._fullName = `{cmd.Prefix}{aliases[1] or `{service.HttpService:GenerateGUID(false)}-RANDOM_COMMAND`}` - return cmd._fullName - end)() - - local pCooldown_Cache = cmd._playerCooldownCache or (function() - local tab = {} - cmd._playerCooldownCache = tab - return tab - end)() - - local sCooldown_Cache = cmd._serverCooldownCache or (function() - local tab = {} - cmd._serverCooldownCache = tab - return tab - end)() - - local crossCooldown_Cache = cmd._crossCooldownCache or (function() - local tab = {} - cmd._crossCooldownCache = tab - return tab - end)() - - local cooldownIndex = tostring(pDat.Player.UserId) - local pCooldown_playerCache = pCooldown_Cache[cooldownIndex] - local sCooldown_playerCache = sCooldown_Cache[cooldownIndex] - - if playerCooldown and pCooldown_playerCache then - local secsTillPass = os.clock() - pCooldown_playerCache - if secsTillPass < playerCooldown then - return false, string.format("[PlayerCooldown] You must wait %.0f seconds to run the command.", playerCooldown - secsTillPass) - end - end - - if serverCooldown and sCooldown_playerCache then - local secsTillPass = os.clock() - sCooldown_playerCache - if secsTillPass < serverCooldown then - return false, string.format("[ServerCooldown] You must wait %.0f seconds to run the command.", serverCooldown - secsTillPass) + ServerBan = { + Prefix = Settings.Prefix; + Commands = {"serverban", "ban"}; + Args = {"player/user", "reason"}; + Description = "Bans the target player(s) from the server"; + AdminLevel = "Admins"; + Filter = true; + Function = function(plr: Player, args: {string}, data: {any}) + local reason = args[2] or "No reason provided" + for _, v in service.GetPlayers(plr, args[1], { + IsKicking = true; + NoFakePlayer = false; + }) + do + if Admin.CheckAuthority(plr, v, "server-ban", false) then + Admin.AddBan(v, reason, false, plr, "Server") + Functions.LogAdminAction(plr, "Server Ban", v.Name, `Reason: {reason}`) + Functions.Hint(`Server-banned {service.FormatPlayer(v, true)}`, {plr}) end end + end + }; - if crossCooldown then - local playerData = Core.GetPlayer(pDat.Player) or {} - local crossCooldown_Cache = playerData._crossCooldownCache or (function() - local tab = {} - playerData._crossCooldownCache = tab - return tab - end)() - local crossCooldown_playerCache = crossCooldown_Cache[cmdFullName] - - if crossCooldown_playerCache then - local secsTillPass = os.clock() - crossCooldown_playerCache - if secsTillPass < crossCooldown then - return false, string.format("[CrossServerCooldown] You must wait %.0f seconds to run the command.", crossCooldown - secsTillPass) - end + UnBan = { + Prefix = Settings.Prefix; + Commands = {"unserverban", "unban"}; + Args = {"user"}; + Description = "Unbans the target user(s) from the server"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, assert(args[1], "Missing user (argument #1)"), { + UseFakePlayer = true; + }) + do + if Admin.RemoveBan(v) then + Functions.LogAdminAction(plr, "Unban", v.Name, "User has been unbanned.") + Functions.Hint(`{service.FormatPlayer(v, true)} has been unbanned`, {plr}) + else + Functions.Hint(`{service.FormatPlayer(v, true)} is not currently banned`, {plr}) end end end - - return permAllowed, nil - end; - - UpdateCooldown = function(pDat, cmd) - if pDat.Player == "SYSTEM" then return end - local playerCooldown = tonumber(cmd.PlayerCooldown) - local serverCooldown = tonumber(cmd.ServerCooldown) - local crossCooldown = tonumber(cmd.CrossCooldown) - - local cmdFullName = cmd._fullName or (function() - local aliases = cmd.Aliases or cmd.Commands or {} - cmd._fullName = `{cmd.Prefix}{aliases[1] or `{service.HttpService:GenerateGUID(false)}-RANDOM_COMMAND`}` - return cmd._fullName - end)() - - local pCooldown_Cache = cmd._playerCooldownCache or (function() - local tab = {} - cmd._playerCooldownCache = tab - return tab - end)() - - local sCooldown_Cache = cmd._serverCooldownCache or (function() - local tab = {} - cmd._serverCooldownCache = tab - return tab - end)() - - local crossCooldown_Cache = cmd._crossCooldownCache or (function() - local tab = {} - cmd._crossCooldownCache = tab - return tab - end)() - - local cooldownIndex = tostring(pDat.Player.UserId) - local pCooldown_playerCache = pCooldown_Cache[cooldownIndex] - local sCooldown_playerCache = sCooldown_Cache[cooldownIndex] - local lastUsed = os.clock() - - if playerCooldown then - pCooldown_Cache[cooldownIndex] = lastUsed - end - - if serverCooldown then - sCooldown_Cache[cooldownIndex] = lastUsed - end - - --// Cross cooldown - do - local playerData = Core.GetPlayer(pDat.Player) - local crossCooldown_Cache = playerData._crossCooldownCache or {} - local crossCooldown_playerCache = crossCooldown_Cache[cmdFullName] - - if not crossCooldown and crossCooldown_playerCache then - crossCooldown_playerCache[cmdFullName] = nil - elseif crossCooldown then - crossCooldown_Cache[cmdFullName] = lastUsed + }; + + BanMenu = { + Prefix = Settings.Prefix; + Commands = {"banmenu"}; + Args = {}; + Description = "Opens the ban menu"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}, data: {any}) + Remote.MakeGui(plr,"BanMenu",{ + AdminLevel = Admin.GetLevel(plr); + CanBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.ServerBan.AdminLevel); + CanTimeBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.TimeBan.AdminLevel); + CanPermBan = Admin.CheckComLevel(Admin.GetLevel(plr),Commands.PermanentBan.AdminLevel); + Prefix = Functions.GetMainPrefix() + }) + end, + }; + + CustomMessage = { + Prefix = Settings.Prefix; + Commands = {"cm", "custommessage"}; + Args = {"Upper message", "message"}; + Filter = true; + Description = "Same as message but says whatever you want upper message to be instead of your name."; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[1], "Missing message title (argument #1)") + assert(args[2], "Missing message (argument #2)") + for _, v in service.Players:GetPlayers() do + Remote.RemoveGui(v, "Message") + Remote.MakeGui(v, "Message", { + Title = args[1]; + Message = args[2]; + Time = (#tostring(args[1]) / 19) + 2.5; + --service.Filter(args[1],plr, v); + }) end end - end; - - SearchCommands = function(p, search) - local checkPerm = Admin.CheckPermission - local tab = {} - local pDat = { - Player = p; - Level = Admin.GetLevel(p); - isDonor = Admin.CheckDonor(p); - } - - for ind, cmd in Commands do - if checkPerm(pDat, cmd, true) then - tab[ind] = cmd + }; + + Nil = { + Prefix = Settings.Prefix; + Commands = {"nil"}; + Args = {"player"}; + Hidden = true; + Description = `Deletes the player forcefully, causing them to be kicked for "Player has been removed from the DataModel"`; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + v.Character = nil + v.Parent = nil + Functions.Hint(`Sent {service.FormatPlayer(v)} to nil`, {plr}) + end + end + }; + + PromptPremiumPurchase = { + Prefix = Settings.Prefix; + Commands = {"promptpremiumpurchase", "premiumpurchaseprompt"}; + Args = {"player"}; + Description = "Opens the Roblox Premium purchase prompt for the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + service.MarketplaceService:PromptPremiumPurchase(v) + end + end + }; + + RobloxNotify = { + Prefix = Settings.Prefix; + Commands = {"rbxnotify", "robloxnotify", "robloxnotif", "rblxnotify", "rnotif", "rn"}; + Args = {"player", "duration (seconds)", "text"}; + Filter = true; + Description = "Sends a Roblox-styled notification for the target player(s)"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + for _, v in service.GetPlayers(plr, args[1]) do + Remote.Send(v, "Function", "SetCore", "SendNotification", { + Title = "Notification"; + Text = args[3] or "Hello, from Adonis!"; + Duration = tonumber(args[2]) or 5; + }) end end + }; + + Disguise = { + Prefix = Settings.Prefix; + Commands = {"disguise", "masquerade"}; + Args = {"player", "username"}; + Description = "Names the player, chars the player, and modifies the player's chat tag"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + assert(args[2], "Argument missing or nil") + local userId = Functions.GetUserIdFromNameAsync(args[2]) + assert(userId, "Invalid username supplied/user not found") - return tab - end; + local username = select(2, xpcall(function() + return service.Players:GetNameFromUserIdAsync(userId) + end, function() return args[2] end)) - CheckAuthority = function(p, target, actionName, allowSelf) - if p == target then - if allowSelf == false then - Functions.Hint(`You cannot {actionName} yourself`, {p}) - return false + if service.Players:GetPlayerByUserId(userId) then + error("You cannot disguise as this player (currently in server)") end - return allowSelf or Remote.GetGui(p, "YesNoPrompt", { - Question = `Are you sure you want to {actionName} yourself?`; - }) == "Yes" + Commands.Char.Function(plr, args) + Commands.DisplayName.Function(plr, {args[1], username}) - elseif Admin.GetLevel(p) > Admin.GetLevel(target) then - return true - end + local ChatService = Functions.GetChatService() - Functions.Hint(`You don't have permission to {actionName} {service.FormatPlayer(target)}`, {p}) - return false - end; + for _, v in service.GetPlayers(plr, args[1]) do + if Variables.DisguiseBindings[v.UserId] then + Variables.DisguiseBindings[v.UserId].Rename:Disconnect() + Variables.DisguiseBindings[v.UserId].Rename = nil + if ChatService then + ChatService:RemoveSpeaker(Variables.DisguiseBindings[v.UserId].TargetUsername) + ChatService:UnregisterProcessCommandsFunction(`Disguise_{v.Name}`) + end + end - GetAliases = function(player) - local aliases = table.clone(Variables.Aliases) - local pData = player and Core.GetPlayer(player) + Variables.DisguiseBindings[v.UserId] = { + TargetUsername = username; + Rename = v.CharacterAppearanceLoaded:Connect(function(char) + Commands.DisplayName.Function(v, {v.Name, username}) + end); + } - if pData and pData.Aliases then - for alias, command in pData.Aliases do - aliases[alias] = command + if ChatService then + local disguiseSpeaker = ChatService:AddSpeaker(username) + disguiseSpeaker:JoinChannel("All") + ChatService:RegisterProcessCommandsFunction(`Disguise_{v.Name}`, function(speaker, message, channelName) + if speaker == v.Name then + local filteredMessage = select(2, xpcall(function() + return service.TextService:FilterStringAsync(message, v.UserId, Enum.TextFilterContext.PrivateChat):GetChatForUserAsync(v.UserId) + end, function() + Remote.Send(v, "Function", "ChatMessage", "A message filtering error occurred.", Color3.new(1, 64/255, 77/255)) + return + end)) + if filteredMessage and not server.Admin.DoHideChatCmd(v, message) then + disguiseSpeaker:SayMessage(filteredMessage, channelName) + if v.Character then + service.Chat:Chat(v.Character, filteredMessage, Enum.ChatColor.White) + end + end + return true + end + return false + end) + end end end - - return aliases - end; + }; + + UnDisguise = { + Prefix = Settings.Prefix; + Commands = {"undisguise", "removedisguise", "cleardisguise", "nodisguise"}; + Args = {"player"}; + Description = "Removes the player's disguise"; + AdminLevel = "Admins"; + Function = function(plr: Player, args: {string}) + local ChatService = Functions.GetChatService() + for _, v in service.GetPlayers(plr, args[1]) do + if Variables.DisguiseBindings[v.UserId] then + Variables.DisguiseBindings[v.UserId].Rename:Disconnect() + Variables.DisguiseBindings[v.UserId].Rename = nil + pcall(function() + ChatService:RemoveSpeaker(Variables.DisguiseBindings[v.UserId].TargetUsername) + ChatService:UnregisterProcessCommandsFunction(`Disguise_{v.Name}`) + end) + end + Variables.DisguiseBindings[v.UserId] = nil + end + Commands.UnChar.Function(plr, args) + Commands.UnDisplayName.Function(plr, args) + end + }; } end diff --git a/MainModule/Server/Core/Admin.luau b/MainModule/Server/Core/Admin.luau index 551cdbaf8..c38d42b7e 100644 --- a/MainModule/Server/Core/Admin.luau +++ b/MainModule/Server/Core/Admin.luau @@ -425,14 +425,15 @@ return function(Vargs, GetEnv) local pData = data or Core.GetPlayer(p) if pData.Client.HideChatCommands then if Admin.BlankPrefix and - (string.sub(message,1,1) ~= Settings.Prefix or string.sub(message,1,1) ~= Settings.PlayerPrefix) then + + (string.sub(message,1,1) ~= Settings.Prefix or string.sub(message,1,1) ~= Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and not table.find(Settings.Prefix, string.sub(message,1,1)))) then local isCMD = Admin.GetCommand(message) if isCMD then return true else return false end - elseif (string.sub(message,1,1) == Settings.Prefix or string.sub(message,1,1) == Settings.PlayerPrefix) + elseif (string.sub(message,1,1) == Settings.Prefix or string.sub(message,1,1) == Settings.PlayerPrefix or (type(Settings.Prefix) == "table" and table.find(Settings.Prefix, string.sub(message,1,1)))) and string.sub(message,2,2) ~= string.sub(message,1,1) then return true; end @@ -1397,7 +1398,7 @@ return function(Vargs, GetEnv) --// Make it so you can't accidentally overwrite certain existing commands... resulting in being unable to add/edit/remove aliases (and other stuff) CheckAliasBlacklist = function(alias) local playerPrefix = Settings.PlayerPrefix; - local prefix = Settings.Prefix; + local prefix = Settings.Prefix local blacklist = { [`{playerPrefix}alias`] = true; [`{playerPrefix}newalias`] = true;