From 2264546d153bd459cf39f01007fca4f8f2ecd4e0 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 12:48:03 -0500 Subject: [PATCH 01/15] add multiple prefix support --- Loader/Config/Settings.luau | 2 +- MainModule/Server/Core/Admin.luau | 39 ++++++++++++++----- MainModule/Server/Core/Commands.luau | 10 ++++- .../Server/Dependencies/DefaultSettings.luau | 20 +++++----- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Loader/Config/Settings.luau b/Loader/Config/Settings.luau index 51cc435e0f..f0f56a2f93 100644 --- a/Loader/Config/Settings.luau +++ b/Loader/Config/Settings.luau @@ -238,7 +238,7 @@ settings.SaveAdmins = true -- If true anyone you :admin or :headadmin in-game settings.LoadAdminsFromDS = true -- If false, any admins saved in your DataStores will not load settings.WhitelistEnabled = false -- If true enables the whitelist/server lock; Only lets admins & whitelisted users join -settings.Prefix = ":" -- The : in :kill me +settings.Prefix = {";", ":"} -- A list of prefixes for commands, the ; in ;kill me settings.PlayerPrefix = "!" -- The ! in !donate; Mainly used for commands that any player can run; Do not make it the same as settings.Prefix settings.SpecialPrefix = "" -- Used for things like "all", "me" and "others" (If changed to ! you would do :kill !me) settings.SplitKey = " " -- The space in :kill me (eg if you change it to / :kill me would be :kill/me) diff --git a/MainModule/Server/Core/Admin.luau b/MainModule/Server/Core/Admin.luau index 0ad9ff97ae..551cdbaf8f 100644 --- a/MainModule/Server/Core/Admin.luau +++ b/MainModule/Server/Core/Admin.luau @@ -1193,9 +1193,16 @@ return function(Vargs, GetEnv) for ind, data in Commands do if type(data) == "table" then for i,cmd in data.Commands do - if data.Prefix == "" then Admin.BlankPrefix = true end - tempPrefix[data.Prefix] = true - tempTable[string.lower(data.Prefix..cmd)] = ind + 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 + end end end end @@ -1232,13 +1239,27 @@ return function(Vargs, GetEnv) end for _, v in data.Commands do - if not blacklistedCommands["/"..data.Prefix..v] then - if not command1 then - command1 = "/"..data.Prefix..v - else - command2 = "/"..data.Prefix..v + 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 + end + end if command1 then @@ -1336,7 +1357,7 @@ return function(Vargs, GetEnv) FormatCommand = function(command, cmdn) return table.concat({ - (command.Prefix or ""), + (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 "" diff --git a/MainModule/Server/Core/Commands.luau b/MainModule/Server/Core/Commands.luau index c8edb07110..e9b9ad82b8 100644 --- a/MainModule/Server/Core/Commands.luau +++ b/MainModule/Server/Core/Commands.luau @@ -34,7 +34,7 @@ return function(Vargs, GetEnv) t = server.Typechecker; local ValidateCommandDefinition = t.interface({ - Prefix = t.string, + Prefix = t.union(t.string, t.array(t.string)), Commands = t.array(t.string), Description = t.string, AdminLevel = t.union(t.string, t.number, t.nan, t.array(t.union(t.string, t.number, t.nan))), @@ -99,7 +99,13 @@ return function(Vargs, GetEnv) Admin.PrefixCache[cmd.Prefix] = true for _, v in cmd.Commands do - Admin.CommandCache[string.lower(cmd.Prefix..v)] = ind + if type(cmd.Prefix) == "table" then + for _,p in cmd.Prefix do + Admin.CommandCache[string.lower(p..v)] = ind + end + else + Admin.CommandCache[string.lower(cmd.Prefix..v)] = ind + end end cmd.Args = cmd.Args or cmd.Arguments or {} diff --git a/MainModule/Server/Dependencies/DefaultSettings.luau b/MainModule/Server/Dependencies/DefaultSettings.luau index cfc7bb1e61..f0f56a2f93 100644 --- a/MainModule/Server/Dependencies/DefaultSettings.luau +++ b/MainModule/Server/Dependencies/DefaultSettings.luau @@ -140,14 +140,13 @@ settings.HideScript = true -- When the game starts the Adonis_Loader model settings.DataStore = "Adonis_1" -- DataStore the script will use for saving data; Changing this will lose any saved data settings.DataStoreKey = "CHANGE_THIS" -- CHANGE THIS TO ANYTHING RANDOM! Key used to encrypt all datastore entries; Changing this will lose any saved data settings.DataStoreEnabled = true -- Disable if you don't want to load settings and admins from the datastore; PlayerData will still save -settings.LocalDatastore = false -- If this is turned on, a mock DataStore will forcibly be used instead and shall never save across servers +settings.LocalDatastore = false -- If this is turned on, a mock DataStore will forcibly be used instead and shall never save across servers -settings.Storage = game:GetService("ServerStorage") -- Where things like tools are stored -settings.RecursiveTools = false -- Whether tools that are included in sub-containers within settings.Storage will be available via the :give command (useful if your tools are organized into multiple folders) +settings.Storage = game:GetService("ServerStorage") -- Where things like tools are stored +settings.RecursiveTools = false -- Whether tools that are included in sub-containers within settings.Storage will be available via the :give command (useful if your tools are organized into multiple folders) settings.Theme = "Default" -- UI theme; settings.MobileTheme = "Mobilius" -- Theme to use on mobile devices; Some UI elements are disabled -settings.DefaultTheme = "Default" -- Theme to be used as a replacement for "Default". The new replacement theme can still use "Default" as its Base_Theme however any other theme that references "Default" as its redirects to this theme. --[[ **HOW TO ADD ADMINISTRATORS:** @@ -158,7 +157,7 @@ settings.DefaultTheme = "Default" -- Theme to be used as a replacement for "Defa settings.Ranks = { ["Moderators"] = { Level = 100; - Users = {"Username"; "Username:UserId"; UserId; "Group:GroupId:GroupRank"; "Group:GroupId"; "Item:ItemID"; "GamePass:GamePassID";} + Users = {"Username"; "Username:UserId"; UserId; "Group:GroupId:GroupRank"; "Group:GroupId"; "Item:ItemID"; "GamePass:GamePassID"; "Subscription:SubscriptionId";} } } @@ -211,7 +210,6 @@ settings.Aliases = { [":examplealias "] = ":ff | :fling | :fire " --// Order arguments appear in alias string determines their required order in the command message when ran later }; ---// Use the below table to define pre-set cameras settings.Cameras = { --[[ "Camera Name" would be the name of your camera @@ -240,7 +238,7 @@ settings.SaveAdmins = true -- If true anyone you :admin or :headadmin in-game settings.LoadAdminsFromDS = true -- If false, any admins saved in your DataStores will not load settings.WhitelistEnabled = false -- If true enables the whitelist/server lock; Only lets admins & whitelisted users join -settings.Prefix = ":" -- The : in :kill me +settings.Prefix = {";", ":"} -- A list of prefixes for commands, the ; in ;kill me settings.PlayerPrefix = "!" -- The ! in !donate; Mainly used for commands that any player can run; Do not make it the same as settings.Prefix settings.SpecialPrefix = "" -- Used for things like "all", "me" and "others" (If changed to ! you would do :kill !me) settings.SplitKey = " " -- The space in :kill me (eg if you change it to / :kill me would be :kill/me) @@ -295,7 +293,7 @@ settings.ChatCommands = true -- If false you will not be able to run commands settings.CreatorPowers = true -- Gives me creator-level admin; This is strictly used for debugging; I can't debug without full access to the script settings.CodeExecution = true -- Enables the use of code execution in Adonis; Scripting related (such as :s) and a few other commands require this settings.SilentCommandDenials = false -- If true, there will be no differences between the error messages shown when a user enters an invalid command and when they have insufficient permissions for the command -settings.OverrideChatCallbacks = true -- If the TextChatService ShouldDeliverCallbacks of all channels are overridden by Adonis on load. Required for slowmode. Mutes use a CanSend method to mute when this is set to false. +settings.OverrideChatCallbacks = true -- If the TextChatService ShouldDeliverCallbacks of all channels are overridden by Adonis on load. Required for slowmode. Mutes use a CanSend method to mute when this is set to false. settings.BanMessage = "Banned" -- Message shown to banned users upon kick settings.LockMessage = "Not Whitelisted" -- Message shown to people when they are kicked while the game is :slocked @@ -306,6 +304,7 @@ settings.SaveCommandLogs = true -- If command logs are saved to the d settings.Notification = true -- Whether or not to show the "You're an admin" and "Updated" notifications settings.SongHint = true -- Display a hint with the current song name and ID when a song is played via :music settings.TopBarShift = false -- By default hints and notifications will appear from the top edge of the window. Set this to true if you don't want hints/notifications to appear in that region. +settings.DefaultTheme = "Default" -- Theme to be used as a replacement for "Default". The new replacement theme can still use "Default" as its Base_Theme however any other theme that references "Default" as its redirects to this theme. settings.HiddenThemes = {} -- Hide themes from the theme selector tab inside the userpanel. Each theme name must be the specific name such as "Mobilius" settings.Messages = {} -- A list of notifications shown on join. Messages can either be strings or tables. Messages are shown to HeadAdmins+ by default but tables can define a different minimum level via .Level settings.AutoClean = false -- Will auto clean workspace of things like hats and tools @@ -408,7 +407,6 @@ descs.RecursiveTools = [[ Whether tools that are included in sub-containers with descs.Theme = [[ UI theme; ]] descs.MobileTheme = [[ Theme to use on mobile devices; Mobile themes are optimized for smaller screens; Some GUIs are disabled ]] -descs.DefaultTheme = [[ Theme to be used as a replacement for "Default". The new replacement theme can still use "Default" as its Base_Theme however any other theme that references "Default" as its redirects to this theme. ]] descs.Ranks = [[ All admin permission level ranks; ]]; descs.Moderators = [[ Mods; Format: {"Username"; "Username:UserId"; UserId; "Group:GroupId:GroupRank"; "Group:GroupId"; "Item:ItemID";} ]] @@ -481,6 +479,7 @@ descs.Notification = [[ Whether or not to show the "You're an admin" and "Update descs.CodeExecution = [[ Enables the use of code execution in Adonis; Scripting related and a few other commands require this ]] descs.SongHint = [[ Display a hint with the current song name and ID when a song is played via :music ]] descs.TopBarShift = [[ By default hints and notifs will appear from the top edge of the window. Set this to true if you don't want hints/notifications to appear in that region. ]] +descs.DefaultTheme = [[ Theme to be used as a replacement for "Default". The new replacement theme can still use "Default" as its Base_Theme however any other theme that references "Default" as its redirects to this theme. ]] descs.ReJail = [[ If true then when a player rejoins they'll go back into jail. Or if the moderator leaves everybody gets unjailed ]] descs.Messages = [[ A list of notifications shown on join. Messages can either be strings or tables. Messages are shown to HeadAdmins+ by default but tables can define a different minimum level via .Level ]] @@ -530,7 +529,6 @@ order = { " "; "Theme"; "MobileTheme"; - "DefaultTheme"; " "; "Ranks"; " "; @@ -598,6 +596,7 @@ order = { "Notification"; "SongHint"; "TopBarShift"; + "DefaultTheme"; "ReJail"; ""; "AutoClean"; @@ -605,6 +604,7 @@ order = { "AutoBackup"; " "; "PlayerList"; + " "; "Console"; "Console_AdminsOnly"; " "; From 8bb2d5ff7c610a413e66d07abe07f0ae5c12b8fa Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 13:51:11 -0500 Subject: [PATCH 02/15] fox ;cmdinfo --- MainModule/Server/Commands/Players.luau | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MainModule/Server/Commands/Players.luau b/MainModule/Server/Commands/Players.luau index 233703bd1b..d0ee68dece 100644 --- a/MainModule/Server/Commands/Players.luau +++ b/MainModule/Server/Commands/Players.luau @@ -31,11 +31,11 @@ return function(Vargs, env) for _,alias in cmd.Commands do if #cmdAliases >= 4 and (#cmd.Commands - #cmdAliases) ~= 0 then - table.insert(cmdAliases, `and {#cmd.Commands - #cmdAliases} more...`) + table.insert(cmdAliases, `and {#cmd.Commands - #cmdAliases} more...`) break end end - + table.remove(cmdAliases, 1) local permissionDesc = Admin.FormatCommandAdminLevel(cmd) @@ -83,7 +83,7 @@ return function(Vargs, env) local cmd, ind for i, v in Admin.SearchCommands(plr, "all") do for _, p in v.Commands do - if (v.Prefix or "")..string.lower(p) == string.lower(args[1]) then + if (if type(v.Prefix) == "table" then v.Prefix[1] else v.Prefix or "")..string.lower(p) == string.lower(args[1]) then cmd, ind = v, i break end @@ -107,7 +107,7 @@ return function(Vargs, env) Title = "Command Info"; Icon = server.MatIcons.Info; Table = { - {Text = `Prefix: {cmd.Prefix}`, Desc = "Prefix used to run the command"}, + {Text = `Prefix: {if type(cmd.Prefix) =="table" then table.concat(cmd.Prefix, ", ") else cmd.Prefix}`, Desc = "Prefix used to run the command"}, {Text = `Commands: {SanitizeXML(table.concat(cmd.Commands, ", "))}`, Desc = "Valid default aliases for the command"}, {Text = `Arguments: {if cmdArgs == "" then "-" else SanitizeXML(cmdArgs)}`, Desc = "Parameters taken by the command"}, {Text = `Admin Level: {Admin.FormatCommandAdminLevel(cmd)}`, Desc = "Rank required to run the command"}, From cef4bb8dac35b383b31a18d7f7b5d5bfb67ddce4 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:06:33 -0500 Subject: [PATCH 03/15] Fix player command reference --- MainModule/Server/Commands/Players.luau | 35 +++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/MainModule/Server/Commands/Players.luau b/MainModule/Server/Commands/Players.luau index d0ee68dece..3ef8f1fb8a 100644 --- a/MainModule/Server/Commands/Players.luau +++ b/MainModule/Server/Commands/Players.luau @@ -150,7 +150,7 @@ return function(Vargs, env) Description = "Shows you the command prefix using the :cmds command"; AdminLevel = "Players"; Function = function(plr: Player, args: {string}) - Functions.Hint(`{Settings.Prefix}cmds`, {plr}) + Functions.Hint(`{if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix}cmds`, {plr}) end }; @@ -567,16 +567,17 @@ return function(Vargs, env) Description = "Shows you how to use some syntax related things"; AdminLevel = "Players"; Function = function(plr: Player, args: {string}) + local Prefix = if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix local usage = { ""; "Mouse over things in lists to expand them"; "You can also resize windows by dragging the edges"; ""; - `Put /e in front to silence commands in chat (/e {Settings.Prefix}kill scel) or enable chat command hiding in client settings`; + `Put /e in front to silence commands in chat (/e {Prefix}kill scel) or enable chat command hiding in client settings`; `Player commands can be used by anyone, these commands have {Settings.PlayerPrefix} infront, such as {Settings.PlayerPrefix}info and {Settings.PlayerPrefix}rejoin`; ""; `――――― Player Selectors ―――――`; - `Usage example: {Settings.Prefix}kill {Settings.SpecialPrefix}all (where {Settings.SpecialPrefix}all is the selector)`; + `Usage example: {Prefix}kill {Settings.SpecialPrefix}all (where {Settings.SpecialPrefix}all is the selector)`; `{Settings.SpecialPrefix}me - Yourself`; `{Settings.SpecialPrefix}all - Everyone in the server`; `{Settings.SpecialPrefix}admins - All admins in the server`; @@ -584,29 +585,29 @@ return function(Vargs, env) `{Settings.SpecialPrefix}others - Everyone except yourself`; `{Settings.SpecialPrefix}random - A random person in the server excluding those removed with -SELECTION`; `@USERNAME - Targets a specific player with that exact username Ex: {Settings.Prefix}god @Sceleratis would give a player with the username 'Sceleratis' god powers`; - `#NUM - NUM random players in the server {Settings.Prefix}ff #5 will ff 5 random players excluding those removed with -SELECTION.`; + `#NUM - NUM random players in the server {Prefix}ff #5 will ff 5 random players excluding those removed with -SELECTION.`; `{Settings.SpecialPrefix}friends - Your friends who are in the server`; - `%TEAMNAME - Members of the team TEAMNAME Ex: {Settings.Prefix}kill %raiders`; + `%TEAMNAME - Members of the team TEAMNAME Ex: {Prefix}kill %raiders`; `$GROUPID - Members of the group with ID GROUPID (number in the Roblox group webpage URL)`; - `-SELECTION - Inverts the selection, ie. will remove SELECTION from list of players to run command on. {Settings.Prefix}kill all,-%TEAM will kill everyone except players on TEAM`; - `+SELECTION - Readds the selection, ie. will readd SELECTION from list of players to run command on. {Settings.Prefix}kill all,-%TEAM,+Lethalitics will kill everyone except players on TEAM but also Lethalitics`; - `radius-NUM -- Anyone within a NUM-stud radius of you. {Settings.Prefix}ff radius-5 will ff anyone within a 5-stud radius of you.`; + `-SELECTION - Inverts the selection, ie. will remove SELECTION from list of players to run command on. {Prefix}kill all,-%TEAM will kill everyone except players on TEAM`; + `+SELECTION - Readds the selection, ie. will readd SELECTION from list of players to run command on. {Prefix}kill all,-%TEAM,+Lethalitics will kill everyone except players on TEAM but also Lethalitics`; + `radius-NUM -- Anyone within a NUM-stud radius of you. {Prefix}ff radius-5 will ff anyone within a 5-stud radius of you.`; ""; `――――― Repetition ―――――`; - `Multiple player selections - {Settings.Prefix}kill me,noob1,noob2,{Settings.SpecialPrefix}random,%raiders,$123456,{Settings.SpecialPrefix}nonadmins,-scel`; - `Multiple Commands at a time - {Settings.Prefix}ff me {Settings.BatchKey} {Settings.Prefix}sparkles me {Settings.BatchKey} {Settings.Prefix}rocket jim`; - `You can add a delay if you want; {Settings.Prefix}ff me {Settings.BatchKey} !wait 10 {Settings.BatchKey} {Settings.Prefix}m hi we waited 10 seconds`; + `Multiple player selections - {Prefix}kill me,noob1,noob2,{Settings.SpecialPrefix}random,%raiders,$123456,{Settings.SpecialPrefix}nonadmins,-scel`; + `Multiple Commands at a time - {Prefix}ff me {Settings.BatchKey} {Settings.Prefix}sparkles me {Settings.BatchKey} {Prefix}rocket jim`; + `You can add a delay if you want; {Prefix}ff me {Settings.BatchKey} !wait 10 {Settings.BatchKey} {Prefix}m hi we waited 10 seconds`; `{Settings.Prefix}repeat 10(how many times to run the cmd) 1(how long in between runs) {Settings.Prefix}respawn jim`; ""; `――――― Reference Info ―――――`; - `{Settings.Prefix}cmds for a list of available commands`; - `{Settings.Prefix}cmdinfo <command> for detailed info about a specific command`; + `{Prefix}cmds for a list of available commands`; + `{Prefix}cmdinfo <command> for detailed info about a specific command`; `{Settings.PlayerPrefix}brickcolors for a list of BrickColors`; `{Settings.PlayerPrefix}materials for a list of materials`; ""; - `{Settings.Prefix}capes for a list of preset admin capes`; - `{Settings.Prefix}musiclist for a list of preset audios`; - `{Settings.Prefix}insertlist for a list of insertable assets using {Settings.Prefix}insert`; + `{Prefix}capes for a list of preset admin capes`; + `{Prefix}musiclist for a list of preset audios`; + `{Prefix}insertlist for a list of insertable assets using {Prefix}insert`; } Remote.MakeGui(plr, "List", { Title = "Usage"; @@ -937,7 +938,7 @@ return function(Vargs, env) IsDonor = Admin.CheckDonor(v); GameData = gameData; IsServerOwner = v.UserId == game.PrivateServerOwnerId; - CmdPrefix = Settings.Prefix; + CmdPrefix = Settings.Prefix[1]; CmdSplitKey = Settings.SplitKey; OnlineFriends = Remote.Get(v, "Function", "GetFriendsOnline"); }) From d0a3e668e2bcbdf3fb75891856488585238c176b Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:12:26 -0500 Subject: [PATCH 04/15] fix all players command + new func --- MainModule/Server/Commands/Players.luau | 10 +++++----- MainModule/Server/Core/Functions.luau | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/MainModule/Server/Commands/Players.luau b/MainModule/Server/Commands/Players.luau index 3ef8f1fb8a..ee7ac02dbd 100644 --- a/MainModule/Server/Commands/Players.luau +++ b/MainModule/Server/Commands/Players.luau @@ -584,7 +584,7 @@ return function(Vargs, env) `{Settings.SpecialPrefix}nonadmins - Non-admins (normal players) in the server`; `{Settings.SpecialPrefix}others - Everyone except yourself`; `{Settings.SpecialPrefix}random - A random person in the server excluding those removed with -SELECTION`; - `@USERNAME - Targets a specific player with that exact username Ex: {Settings.Prefix}god @Sceleratis would give a player with the username 'Sceleratis' god powers`; + `@USERNAME - Targets a specific player with that exact username Ex: {Prefix}god @Sceleratis would give a player with the username 'Sceleratis' god powers`; `#NUM - NUM random players in the server {Prefix}ff #5 will ff 5 random players excluding those removed with -SELECTION.`; `{Settings.SpecialPrefix}friends - Your friends who are in the server`; `%TEAMNAME - Members of the team TEAMNAME Ex: {Prefix}kill %raiders`; @@ -595,9 +595,9 @@ return function(Vargs, env) ""; `――――― Repetition ―――――`; `Multiple player selections - {Prefix}kill me,noob1,noob2,{Settings.SpecialPrefix}random,%raiders,$123456,{Settings.SpecialPrefix}nonadmins,-scel`; - `Multiple Commands at a time - {Prefix}ff me {Settings.BatchKey} {Settings.Prefix}sparkles me {Settings.BatchKey} {Prefix}rocket jim`; + `Multiple Commands at a time - {Prefix}ff me {Settings.BatchKey} {Prefix}sparkles me {Settings.BatchKey} {Prefix}rocket jim`; `You can add a delay if you want; {Prefix}ff me {Settings.BatchKey} !wait 10 {Settings.BatchKey} {Prefix}m hi we waited 10 seconds`; - `{Settings.Prefix}repeat 10(how many times to run the cmd) 1(how long in between runs) {Settings.Prefix}respawn jim`; + `{Prefix}repeat 10(how many times to run the cmd) 1(how long in between runs) {Prefix}respawn jim`; ""; `――――― Reference Info ―――――`; `{Prefix}cmds for a list of available commands`; @@ -938,7 +938,7 @@ return function(Vargs, env) IsDonor = Admin.CheckDonor(v); GameData = gameData; IsServerOwner = v.UserId == game.PrivateServerOwnerId; - CmdPrefix = Settings.Prefix[1]; + CmdPrefix = Functions.GetMainPrefix(); CmdSplitKey = Settings.SplitKey; OnlineFriends = Remote.Get(v, "Function", "GetFriendsOnline"); }) @@ -1019,7 +1019,7 @@ return function(Vargs, env) ServerAge = service.FormatTime(os.time() - server.ServerStartTime); ServerInternetInfo = serverInfo; Refreshables = Logs.ListUpdaters.ServerDetails(plr); - CmdPrefix = Settings.Prefix; + CmdPrefix = if type(Settings.Prefix) == "table" then Settings.Prefix[1] else Settings.Prefix, CmdPlayerPrefix = Settings.PlayerPrefix; SplitKey = Settings.SplitKey; }) diff --git a/MainModule/Server/Core/Functions.luau b/MainModule/Server/Core/Functions.luau index e81407b0e3..f137444a85 100644 --- a/MainModule/Server/Core/Functions.luau +++ b/MainModule/Server/Core/Functions.luau @@ -1664,5 +1664,11 @@ return function(Vargs, GetEnv) end return if allowNil then nil else BrickColor.random() end; - }; + + GetMainPrefix = function() + if type(Settings.Prefix) == "table" then return Settings.Prefix[1] else return Settings.Prefix end + end; + } + + end From 51a7b292417acf20eabeabc961305fc43605f77a Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:20:03 -0500 Subject: [PATCH 05/15] hacky metatable patch --- MainModule/Server/Server.luau | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MainModule/Server/Server.luau b/MainModule/Server/Server.luau index 486f3ca2ae..8fa9d74b23 100644 --- a/MainModule/Server/Server.luau +++ b/MainModule/Server/Server.luau @@ -597,6 +597,17 @@ return service.NewProxy({ end end + --// Attempts to patch Settings.Prefix to fix issues + if type(server.Settings.Prefix == "table") then + setmetatable(server.Settings.Prefix, { + __concat = function(self, value) + return self[1] + end, + }) + end + + + --// Bind cleanup service.DataModel:BindToClose(function(...) server.CleanUp(...) From f51eb93052a359501fa4d44fd97bda67b90a5604 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:23:13 -0500 Subject: [PATCH 06/15] metatables more like magictables --- MainModule/Server/Server.luau | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MainModule/Server/Server.luau b/MainModule/Server/Server.luau index 8fa9d74b23..fe3e58a58e 100644 --- a/MainModule/Server/Server.luau +++ b/MainModule/Server/Server.luau @@ -603,11 +603,16 @@ return service.NewProxy({ __concat = function(self, value) return self[1] end, + __tostring = function(self) + return self[1] + end, + }) end + --// Bind cleanup service.DataModel:BindToClose(function(...) server.CleanUp(...) From 324e012321ae7e62b8a10f903fb11a9c0ba8a382 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:24:25 -0500 Subject: [PATCH 07/15] oops --- MainModule/Server/Server.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MainModule/Server/Server.luau b/MainModule/Server/Server.luau index fe3e58a58e..393b3ea1aa 100644 --- a/MainModule/Server/Server.luau +++ b/MainModule/Server/Server.luau @@ -598,7 +598,7 @@ return service.NewProxy({ end --// Attempts to patch Settings.Prefix to fix issues - if type(server.Settings.Prefix == "table") then + if type(server.Settings.Prefix) == "table" then setmetatable(server.Settings.Prefix, { __concat = function(self, value) return self[1] From 640cbc6b1ab96c8440d7c615fc3600428472557a Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:27:15 -0500 Subject: [PATCH 08/15] fix some moderator commands --- MainModule/Server/Commands/Moderators.luau | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MainModule/Server/Commands/Moderators.luau b/MainModule/Server/Commands/Moderators.luau index 95b0420985..41690e9f5b 100644 --- a/MainModule/Server/Commands/Moderators.luau +++ b/MainModule/Server/Commands/Moderators.luau @@ -292,7 +292,7 @@ return function(Vargs, env) Commands = {"cn", "customsmallmessage", "cnmessage"}; Args = {"title", "message"}; Filter = true; - Description = `Same as {Settings.Prefix}n but says whatever you want the title to be instead of your name.`; + Description = `Same as {Functions.GetMainPrefix()}n but says whatever you want the title to be instead of your name.`; AdminLevel = "Moderators"; Function = function(plr: Player, args: {string}) Functions.Notify(service.BroadcastFilter(assert(args[1], "Missing title"), plr), service.BroadcastFilter(assert(args[2], "Missing message") , plr), service.GetPlayers()) @@ -377,7 +377,7 @@ return function(Vargs, env) Remote.RemoveGui(v, "Notify") Functions.Notify(`Warning from {service.FormatPlayer(plr)}`, reason, {v}) - Functions.Notification("Notification", `Warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}warnings {v.Name}')`)) + Functions.Notification("Notification", `Warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}warnings {v.Name}')`)) end end end @@ -416,7 +416,7 @@ return function(Vargs, env) Core.CrossServer("RemovePlayer", v.Name, `Warning from {service.FormatPlayer(plr)}`, reason) end - Functions.Notification("Notification", `Kick-warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}warnings {v.Name}')`)) + Functions.Notification("Notification", `Kick-warned {service.FormatPlayer(v)}`, {plr}, 5, "MatIcons://Shield", Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}warnings {v.Name}')`)) end end end @@ -725,7 +725,7 @@ return function(Vargs, env) for k, t in v.Backpack:GetChildren() do t.Parent = tools end - Admin.RunCommand(`{Settings.Prefix}name`, v.Name, `-AFK-_{service.FormatPlayer(v)}_-AFK-`) + Admin.RunCommand(`{Functions.GetMainPrefix()}name`, v.Name, `-AFK-_{service.FormatPlayer(v)}_-AFK-`) local torso = v.Character.HumanoidRootPart local pos = torso.CFrame local running=true @@ -738,7 +738,7 @@ return function(Vargs, env) for k, t in tools:GetChildren() do t.Parent = v.Backpack end - Admin.RunCommand(`{Settings.Prefix}unname`, v.Name) + Admin.RunCommand(`{Functions.GetMainPrefix()}unname`, v.Name) event:Disconnect() end) repeat torso.CFrame = pos wait() until not v or not v.Character or not torso or not running or not torso.Parent @@ -811,7 +811,7 @@ return function(Vargs, env) Prefix = Settings.Prefix; Commands = {"fullgod", "totalgod"}; Args = {"player"}; - Description = `Same as {server.Settings.Prefix}god, but also provides blast protection`; + Description = `Same as {Functions.GetMainPrefix()}god, but also provides blast protection`; AdminLevel = "Moderators"; Function = function(plr: Player, args: {string}) for _, v in service.GetPlayers(plr, args[1]) do @@ -1243,7 +1243,7 @@ return function(Vargs, env) TitleButtons = { { Text = ""; - OnClick = Core.Bytecode(`client.Remote.Send('ProcessCommand','{Settings.Prefix}tools')`); + OnClick = Core.Bytecode(`client.Remote.Send('ProcessCommand','{Functions.GetMainPrefix()}tools')`); Children = { { Class = "ImageLabel"; @@ -1594,7 +1594,7 @@ return function(Vargs, env) local command = args[3] local name = string.lower(plr.Name) assert(command, "Missing command name to repeat") - if string.lower(string.sub(command, 1, #Settings.Prefix+string.len("repeat"))) == string.lower(`{Settings.Prefix}repeat`) or string.sub(command, 1, #Settings.Prefix+string.len("loop")) == string.lower(`{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}repeat`) then + if string.lower(string.sub(command, 1, #Functions.GetMainPrefix()+string.len("repeat"))) == string.lower(`{Settings.Prefix}repeat`) or string.sub(command, 1, #Functions.GetMainPrefix()+string.len("loop")) == string.lower(`{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}loop`) or string.find(command, `^{Settings.Prefix}repeat`) then error("Cannot repeat the loop command in a loop command") return end From c14a9f3035cc20a40c4336d9a5edcb7c9d2f202d Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:30:36 -0500 Subject: [PATCH 09/15] fix all moderator commands --- MainModule/Server/Commands/Moderators.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MainModule/Server/Commands/Moderators.luau b/MainModule/Server/Commands/Moderators.luau index 41690e9f5b..1dff5e8411 100644 --- a/MainModule/Server/Commands/Moderators.luau +++ b/MainModule/Server/Commands/Moderators.luau @@ -2116,7 +2116,7 @@ return function(Vargs, env) local data = { Tools = {}; SavedTools = {}; - Prefix = Settings.Prefix; + Prefix = Functions.GetMainPrefix(); SplitKey = Settings.SplitKey; SpecialPrefix = Settings.SpecialPrefix; } @@ -4468,7 +4468,7 @@ return function(Vargs, env) AdminLevel = "Moderators"; Function = function(plr: Player, args: {[number]:string}) Remote.MakeGui(plr, "Teams", { - CmdPrefix = Settings.Prefix; CmdPlayerPrefix = Settings.PlayerPrefix; CmdSpecialPrefix = Settings.SpecialPrefix; CmdSplitKey = Settings.SplitKey; + CmdPrefix = Functions.GetMainPrefix(); CmdPlayerPrefix = Settings.PlayerPrefix; CmdSpecialPrefix = Settings.SpecialPrefix; CmdSplitKey = Settings.SplitKey; }) end }; From 66dc8caba78222d3f0906fbece95158595079cf1 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:34:58 -0500 Subject: [PATCH 10/15] fix all commands --- MainModule/Server/Commands/Admins.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau index f3312c057b..150654bf7e 100644 --- a/MainModule/Server/Commands/Admins.luau +++ b/MainModule/Server/Commands/Admins.luau @@ -684,7 +684,7 @@ return function(Vargs, env) Functions.Hint(string.format("Team '%s' already exists!", teamName), {plr}) return; end - + service.New("Team", { Parent = service.Teams; Name = teamName; @@ -1430,7 +1430,7 @@ return function(Vargs, env) 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 = Settings.Prefix; + Prefix = Functions.GetMainPrefix() }) end, }; From fe80179205c6986c12dbc3c8274ff551210a7c0b Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:41:41 -0500 Subject: [PATCH 11/15] fix admin core --- MainModule/Server/Commands/Admins.luau | 2939 ++++++++++++------------ 1 file changed, 1530 insertions(+), 1409 deletions(-) diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau index 150654bf7e..c38d42b7ed 100644 --- a/MainModule/Server/Commands/Admins.luau +++ b/MainModule/Server/Commands/Admins.luau @@ -1,1598 +1,1719 @@ ---!nocheck -return function(Vargs, env) +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) + local server = Vargs.Server; local service = Vargs.Service; - 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 + 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 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 - Functions.Hint(`You do not have permission to set the rank of {service.FormatPlayer(p, true)}`, {plr}) + 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 + end end - 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 + + --// 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 - 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 + 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 - 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 function onTextChannelsAdded(textChannels) + textChannels.ChildAdded:Connect(function(child) + if child:IsA("TextChannel") then + task.spawn(onNewTextchannel, child) + end + 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}) + for _, v in textChannels:GetChildren() do + if v:IsA("TextChannel") then + task.spawn(onNewTextchannel, v) + end end end - 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}) + + service.TextChatService.ChildAdded:Connect(function(child) + if child.Name == "TextChannels" then + task.spawn(onTextChannelsAdded, child) end + end) + + if service.TextChatService:FindFirstChild("TextChannels") then + task.spawn(pcall, onTextChannelsAdded, service.TextChatService:FindFirstChild("TextChannels")) end - 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 + + 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 userFound then - return - else - Functions.Hint("User not found in server; searching datastore", {plr}) + if Admin.DoHideChatCmd(speakerPlayer, message) then + return true end - end - for rankName, rankData in Settings.Ranks do - if senderLevel <= rankData.Level then - continue + 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 end - 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 + + 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 end - 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}) + + if Admin.SlowMode then + slowCache[speakerPlayer] = os.time() end - 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 - }; - - 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) + + --// 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 end + Ranks[rank] = data end - }; + end - 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 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; + }; + end + end + end - 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}) + if Settings.CommandCooldowns then + for cmdName, cooldownData in Settings.CommandCooldowns do + local realCmd = Admin.GetCommand(cmdName) + + 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 end end end - }; - - 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 + + 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) end end end - }; + end - 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"; - }) + 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 - }; - - 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 + 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 + 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 else - error("Missing user argument") + return false end - 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") + 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 end - 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 + 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 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 - 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") + 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 end + + Admin.GroupsCache[uid] = cacheTab + return table.clone(cacheTab.Groups) else - Variables.NotifMessage = args[1] - for _, v in service.GetPlayers() do - Remote.MakeGui(v, "Notif", { - Message = Variables.NotifMessage; - }) - end + return table.clone((existCache and existCache.Groups) or {}) end end - }; - - 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; + + 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 end end end - }; - - 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 + + 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 end end end - }; - 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 + 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 - }; - - 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 - for _, obj in clonedDeps:GetChildren() do - obj.Parent = F3X - 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 false + end, - for _, v in service.GetPlayers(plr, args[1]) do - local Backpack = v:FindFirstChildOfClass("Backpack") + IsMuted = function(player) + local DoCheck = Admin.DoCheck + for _, v in Settings.Muted do + if DoCheck(player, v) then + return true + end + end - if Backpack then - F3X:Clone().Parent = Backpack - end + for _, v in HTTP.Trello.Mutes do + if DoCheck(player, v) then + return true end + end - F3X:Destroy() + if HTTP.WebPanel.Mutes then + for _, v in HTTP.WebPanel.Mutes do + if DoCheck(player, v) then + return true + 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 - }; - 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]) + if cType == "number" then + return plr.UserId == check + elseif cType == "string" then + if plr.Name == check then + return true + end - for i, v in Variables.InsertList do - if id == string.lower(v.Name)then - id = v.ID - break + 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 + 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 end - end - for i, v in HTTP.Trello.InsertList do - if id == string.lower(v.Name) then - id = v.ID - break + 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 - - 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.") + 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 - }; - - 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 + + return check == plr + end; + + 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; end end - }; + 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 "" + LevelToListName = function(lvl) + if lvl > 999 then + return "Place Owner" + elseif lvl == 0 then + return "Players" + end - if not Variables.MapBackup then - error("Cannot restore when there are no backup maps!", 0) - return + --// 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 - if Variables.RestoringMap then - error("Map has not been backed up",0) - return + end + + for i,v in Settings.Ranks do + if v.Level == lvl then + return i end - if Variables.BackingupMap then - error("Cannot restore map while backing up map is in process!", 0) - return + 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") 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) - Variables.RestoringMap = true - Functions.Hint("Restoring Map...", service.Players:GetPlayers()) - workspace.Gravity = Variables.OriginalGravity + local currentTime = os.time() - for _, obj in workspace:GetChildren() do - if obj.ClassName ~= "Terrain" and not service.Players:GetPlayerFromCharacter(obj) then - obj:Destroy() - service.RunService.Stepped:Wait() + 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}) end end - local new = Variables.MapBackup:Clone() - for _, obj in new:GetChildren() do - obj.Parent = workspace - if obj:IsA("Model") then - obj:MakeJoints() - 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 end - new:Destroy() + 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) + 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 + end + end end + end - task.wait() + if Admin.IsPlaceOwner(p) and highestLevel < 1000 then + return 1000, "Place Owner" + end - Admin.RunCommand(`{Settings.Prefix}fixlighting`) - Admin.RunCommand(`{Settings.Prefix}respawn`, "all") - Variables.RestoringMap = false - Functions.Hint('Map Restore Complete.',service.Players:GetPlayers()) + return highestLevel, highestRank + end; - Logs:AddLog("Script", { - Text = "Map Restoration Complete", - Desc = `{plrName} has restored the map.`, - }) + 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 + end + + if tonumber(CreatorId) and p.UserId == CreatorId then + return true + end + + if p.UserId == -1 and Variables.IsStudio then --// To account for player emulators in multi-client Studio tests + return true + end end - }; - - 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 = {}; + end; + + CheckAdmin = function(p) + return Admin.GetLevel(p) > 0 + end; + + SetLevel = function(p, level, doSave, rankName) + local current, rank = Admin.GetLevel(p) + + if tonumber(level) then + if current >= 1000 then + return false + else + Admin.SpecialLevels[tostring(p.UserId)] = { + Player = p.UserId, + Level = level, + Rank = rankName } - Variables.ScriptBuilder[tostring(plr.UserId)] = sb end + elseif level == "Reset" then + Admin.SpecialLevels[tostring(p.UserId)] = nil + end - local action = string.lower(args[1]) - local class = args[2] or "LocalScript" - local name = args[3] + Admin.UpdateCachedLevel(p) + end; - 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" + IsTempAdmin = function(p) + local DoCheck = Admin.DoCheck + for i,v in Admin.TempAdmins do + if DoCheck(p,v) then + return true, i end + end + end; - if action == "create" then - assert(args[1] and args[2] and args[3], "Missing arguments") - local code = args[4] or " " + 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 - 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 + local isTemp,tempInd = Admin.IsTempAdmin(p) - local wrapped,scr = Core.NewScript(class,code,false,true) + if isTemp then + temp = true + table.remove(Admin.TempAdmins, tempInd) + end - sb[class][name] = { - Wrapped = wrapped; - Script = scr; - } + if override then + temp = false + end - if args[4] then - Functions.Hint(`Created {class} {name} and appended text`, {plr}) - else - Functions.Hint(`Created {class} {name}`, {plr}) - end - 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 - 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}) + 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 - else - error(`{class} {name} not found!`) end - 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 + + 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 + + Admin.RemoveAdmin(p, temp) + Admin.SetLevel(p, level, nil, levelName) + + if temp then + table.insert(Admin.TempAdmins, p) + 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 - 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 + + if index and value then + table.remove(list, index) + end + end + + local value = `{p.Name}:{p.UserId}` + + 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 + }) + 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 - 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 - else - error(`{class} {name} not found!`) + + 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 - 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!`) + + 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 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}) + end + end + end; + + CheckBan = function(p) + local doCheck = Admin.DoCheck + local banCheck = Admin.DoBanCheck + + 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 + + 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) 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}`}) + return true, `\n {ban.Reason or "(No reason provided.)"}\n | Banned until {service.FormatTime(ban.EndTime, {WithWrittenDate = true})}` end + end + end - for i, v in sb.LocalScript do - table.insert(tab, {Text = `LocalScript: {i}`, Desc = `Running: {v.Script.Disabled}`}) - 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 - 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 + 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) 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; + }) - 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 + 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; - Remote.MakeGui(plr,"List",{ - Title = "Starter Scripts"; - Tab = result; - }) + 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, - 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!") + DoBanCheck = function(name: string | number | Instance, check: string | {[string]: any}) + local id = type(name) == "number" and name - 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 + 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 end + else + return string.lower(tostring(check)) == string.lower(tostring(name)) end end - }; - - 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 + + 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; + }) + 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 end - Core.SavePlayer(v, PlayerData) end 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 + + 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 + end + + local blacklistedCommands = {} + + for _, v in container:GetDescendants() do + if v:IsA("TextChatCommand") then + blacklistedCommands[v.PrimaryAlias] = true + blacklistedCommands[v.SecondaryAlias] = true + end + end + + for name, data in Commands do + local command1, command2 = nil, nil + + if type(data) ~= "table" or data.Hidden then + continue + 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 + + end + + 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) + + if player then + Process.Command(player, string.sub(text, 2)) + end + end) + end + end 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) + 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 end - if count == num then - service.StopLoop(`{v.UserId}LOOPKILL`) + 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 - end) + end end + + return foundCmds end - }; + end; - 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`) + SetPermission = function(comString, newLevel) + local cmds = Admin.FindCommands(comString) + if cmds then + for ind, cmd in cmds do + cmd.AdminLevel = newLevel end end - }; + end; - 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 + 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" end end - }; - 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") + 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 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; - 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") + 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 end end end - }; - 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") + 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) end end end - }; - 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") + 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 end end + return false end - }; - 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 + 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 end end - }; + end; - 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") + CheckPermission = function(pDat, cmd, ignoreCooldown, opts) + opts = opts or {} - local title = service.BroadcastFilter(args[2], plr) - assert(title == args[2], "Title was filtered: "..title) + local adminLevel = pDat.Level + local comLevel = cmd.AdminLevel - local msg = service.BroadcastFilter(args[3], plr) - assert(msg == args[3], "Message was filtered: "..msg) + if cmd.Disabled then + return false, "This command has been disabled." + 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 Variables.IsStudio and cmd.NoStudio then + return false, "This command cannot be used in Roblox Studio." + 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 opts.CrossServer and cmd.CrossServerDenied then -- Ignore when disabled then + return false, "This command may not be run across servers (cross-server-blacklisted)." + 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 cmd.CrossServer and not Settings.CrossServerCommands then + return false, "This command has been disabled due to CrossServerCommands being disabled" + 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 Admin.IsPlaceOwner(pDat.Player) or adminLevel >= Settings.Ranks.Creators.Level then + return true, nil + end - local nlogs = #logs - if nlogs > Logs.OldCommandLogsLimit then - table.remove(logs, nlogs) - end + if Admin.IsBlacklisted(pDat.Player) then + return false, "You are blacklisted from running commands." + end - return logs - end) - end + if (comLevel == 0 or comLevel == "Players") and adminLevel <= 0 and not Settings.PlayerCommands then + return false, "Player commands are disabled in this game." + end - Functions.Shutdown(args[1]) + if cmd.Fun and not Settings.FunCommands then + return false, "Fun commands are disabled in this game." end - }; - 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}) + if opts.Chat and cmd.Chattable == false then + return false, "This command is not permitted as chat message (non-chattable command)." + 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 - 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}) + 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) end end - end - }; - - 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); - }) + + 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 + end end end - }; - - 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 + + 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 - }; - 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") + if serverCooldown then + sCooldown_Cache[cooldownIndex] = lastUsed + end - local username = select(2, xpcall(function() - return service.Players:GetNameFromUserIdAsync(userId) - end, function() return args[2] end)) + --// Cross cooldown + do + local playerData = Core.GetPlayer(pDat.Player) + local crossCooldown_Cache = playerData._crossCooldownCache or {} + local crossCooldown_playerCache = crossCooldown_Cache[cmdFullName] - if service.Players:GetPlayerByUserId(userId) then - error("You cannot disguise as this player (currently in server)") + if not crossCooldown and crossCooldown_playerCache then + crossCooldown_playerCache[cmdFullName] = nil + elseif crossCooldown then + crossCooldown_Cache[cmdFullName] = lastUsed end + end + end; - Commands.Char.Function(plr, args) - Commands.DisplayName.Function(plr, {args[1], username}) - - local ChatService = Functions.GetChatService() + SearchCommands = function(p, search) + local checkPerm = Admin.CheckPermission + local tab = {} + local pDat = { + Player = p; + Level = Admin.GetLevel(p); + isDonor = Admin.CheckDonor(p); + } - 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 + for ind, cmd in Commands do + if checkPerm(pDat, cmd, true) then + tab[ind] = cmd + end + end - Variables.DisguiseBindings[v.UserId] = { - TargetUsername = username; - Rename = v.CharacterAppearanceLoaded:Connect(function(char) - Commands.DisplayName.Function(v, {v.Name, username}) - end); - } + return tab + end; - 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 + CheckAuthority = function(p, target, actionName, allowSelf) + if p == target then + if allowSelf == false then + Functions.Hint(`You cannot {actionName} yourself`, {p}) + return false end + + return allowSelf or Remote.GetGui(p, "YesNoPrompt", { + Question = `Are you sure you want to {actionName} yourself?`; + }) == "Yes" + + elseif Admin.GetLevel(p) > Admin.GetLevel(target) then + return true 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 + + Functions.Hint(`You don't have permission to {actionName} {service.FormatPlayer(target)}`, {p}) + return false + end; + + GetAliases = function(player) + local aliases = table.clone(Variables.Aliases) + local pData = player and Core.GetPlayer(player) + + if pData and pData.Aliases then + for alias, command in pData.Aliases do + aliases[alias] = command end - Commands.UnChar.Function(plr, args) - Commands.UnDisplayName.Function(plr, args) end - }; + + return aliases + end; } end From 74e46e671e6b7d42de0e3eab4fd6996dda80420d Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:55:42 -0500 Subject: [PATCH 12/15] fixed __concat --- MainModule/Server/Server.luau | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MainModule/Server/Server.luau b/MainModule/Server/Server.luau index 393b3ea1aa..3294a0e6e6 100644 --- a/MainModule/Server/Server.luau +++ b/MainModule/Server/Server.luau @@ -601,7 +601,7 @@ return service.NewProxy({ if type(server.Settings.Prefix) == "table" then setmetatable(server.Settings.Prefix, { __concat = function(self, value) - return self[1] + return `{self[1]}{value}` end, __tostring = function(self) return self[1] @@ -613,6 +613,7 @@ return service.NewProxy({ + --// Bind cleanup service.DataModel:BindToClose(function(...) server.CleanUp(...) From ace859349073729155cf85588a75084b8f3ed1cf Mon Sep 17 00:00:00 2001 From: SuperCater Date: Sun, 26 Jan 2025 14:59:16 -0500 Subject: [PATCH 13/15] fix userpanel --- MainModule/Client/UI/Default/UserPanel.luau | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MainModule/Client/UI/Default/UserPanel.luau b/MainModule/Client/UI/Default/UserPanel.luau index 7d50c7f385..363fcfbdd8 100644 --- a/MainModule/Client/UI/Default/UserPanel.luau +++ b/MainModule/Client/UI/Default/UserPanel.luau @@ -497,7 +497,8 @@ return function(data, env) chatMod = Remote.Get("Setting",{"Prefix","SpecialPrefix","BatchKey","AnyPrefix","DonorCommands","DonorCapes"}) settingsData = Remote.Get("AllSettings") Variables.Aliases = playerData.Aliases or {} - commandPrefix = chatMod.Prefix + commandPrefix = if type(chatMod.Prefix) == "table" then chatMod.Prefix[1] else chatMod.Prefix + for _, v in loadingIcons do v:Destroy() From 05cb68d82e057e3048d2ed4cf781c575333f29c1 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Mon, 27 Jan 2025 08:57:32 -0500 Subject: [PATCH 14/15] not my brightest moment --- MainModule/Server/Commands/Admins.luau | 2939 ++++++++++++------------ MainModule/Server/Core/Admin.luau | 7 +- 2 files changed, 1413 insertions(+), 1533 deletions(-) diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau index c38d42b7ed..150654bf7e 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 551cdbaf8f..c38d42b7ed 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; From 4fb8aeee4f8b4d3c9634ad8e172c9402752a30a0 Mon Sep 17 00:00:00 2001 From: SuperCater Date: Mon, 27 Jan 2025 09:00:35 -0500 Subject: [PATCH 15/15] balss --- MainModule/Server/Commands/Admins.luau | 2779 +++++++++++------------- 1 file changed, 1320 insertions(+), 1459 deletions(-) diff --git a/MainModule/Server/Commands/Admins.luau b/MainModule/Server/Commands/Admins.luau index 56eefc09ee..150654bf7e 100644 --- a/MainModule/Server/Commands/Admins.luau +++ b/MainModule/Server/Commands/Admins.luau @@ -1,744 +1,688 @@ -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) + 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 - 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 + if env then setfenv(1, env) end - return false - end + local Routine = env.Routine - 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 + 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 + 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`) + 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 - end - end - - service.TextChatService.ChildAdded:Connect(function(child) - if child.Name == "TextChannels" then - task.spawn(onTextChannelsAdded, child) + 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) - - if service.TextChatService:FindFirstChild("TextChannels") then - task.spawn(pcall, onTextChannelsAdded, service.TextChatService:FindFirstChild("TextChannels")) end + end; + }; - AddLog("Script", "TextChatService Handler Loaded") - 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") - --// 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 + assert(newLevel < senderLevel, `Level cannot be equal to or above your own permission level ({senderLevel})`); - if Admin.DoHideChatCmd(speakerPlayer, message) then - return true + 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 + 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 + 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 - 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 + if userFound then + return + else + Functions.Hint("User not found in server; searching datastore", {plr}) end + end - if Admin.SlowMode then - slowCache[speakerPlayer] = os.time() + for rankName, rankData in Settings.Ranks do + if senderLevel <= rankData.Level then + continue 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 + 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 end - Ranks[rank] = data + assert(userFound, `No table entries matching '{args[1]}' were found`) 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; - }; + 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 end - end - - if Settings.CommandCooldowns then - for cmdName, cooldownData in Settings.CommandCooldowns do - local realCmd = Admin.GetCommand(cmdName) + }; - if realCmd then - if cooldownData.Player then - realCmd.PlayerCooldown = cooldownData.Player + 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 + }; - if cooldownData.Server then - realCmd.ServerCooldown = cooldownData.Server - end + 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 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) + 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 - - 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 + 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 - 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 + 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; + 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 + 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 + error("Invalid subcommand (on/off/add/remove/list/clear)") 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 - 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 - end; - - GetGroups = function(uid, updateCache) - uid = tonumber(uid) + }; - if type(uid) == "number" then - local existCache = Admin.GroupsCache[uid] - local canUpdate = false + 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 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 + if args[1] == "off" or args[1] == "false" then + Variables.NotifMessage = nil + for _, v in service.GetPlayers() do + Remote.RemoveGui(v, "Notif") end 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 + Variables.NotifMessage = args[1] + for _, v in service.GetPlayers() do + Remote.MakeGui(v, "Notif", { + Message = Variables.NotifMessage; + }) 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 - end - end + 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 + }; - 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 - end - 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 + }; - 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 + 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 + }; - return false - end, - - IsMuted = function(player) - local DoCheck = Admin.DoCheck - for _, v in Settings.Muted do - if DoCheck(player, v) then - return true + 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 + }; - 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 + }; - if HTTP.WebPanel.Mutes then - for _, v in HTTP.WebPanel.Mutes do - if DoCheck(player, v) then - return true + 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 - 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 + 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 + }; - 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 + }; + + 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]) - --// 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 + for i, v in Variables.InsertList do + if id == string.lower(v.Name)then + id = v.ID + break + end end - end - for i,v in Settings.Ranks do - if v.Level == lvl then - return i + for i, v in HTTP.Trello.InsertList do + if id == string.lower(v.Name) then + id = v.ID + break + end 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") + + 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 + }; - 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}) + 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 - - return newLevel, newRank end + }; - return level or 0, rank; - end; + 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 + }; - GetUpdatedLevel = function(p, data) - local checkTable = Admin.CheckTable - local doCheck = Admin.DoCheck + 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]) - for _, admin in Admin.SpecialLevels do - if doCheck(p, admin.Player) then - return admin.Level, admin.Rank + if service.Teams:FindFirstChild(teamName) then + Functions.Hint(string.format("Team '%s' already exists!", teamName), {plr}) + return; end service.New("Team", { @@ -751,852 +695,728 @@ return function(Vargs, GetEnv) Functions.Hint(string.format("Created new team '%s' (%s)", teamName, teamColor.Name), {plr}) 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 + 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; - - 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 + new:Destroy() - local isTemp,tempInd = Admin.IsTempAdmin(p) + 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 - if isTemp then - temp = true - table.remove(Admin.TempAdmins, tempInd) - end + task.wait() - if override then - temp = false - end + Admin.RunCommand(`{Settings.Prefix}fixlighting`) + Admin.RunCommand(`{Settings.Prefix}respawn`, "all") + Variables.RestoringMap = false + Functions.Hint('Map Restore Complete.',service.Players:GetPlayers()) - if type(p) == "userdata" then - Admin.SetLevel(p, 0) + Logs:AddLog("Script", { + Text = "Map Restoration Complete", + Desc = `{plrName} has restored the map.`, + }) 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 + 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 - - 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 - - Admin.RemoveAdmin(p, temp) - Admin.SetLevel(p, level, nil, levelName) - - if temp then - table.insert(Admin.TempAdmins, p) - end - if list and type(list) == "table" then - local index,value + local action = string.lower(args[1]) + local class = args[2] or "LocalScript" + local name = args[3] - 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 + 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 - if index and value then - table.remove(list, index) - end - end + if action == "create" then + assert(args[1] and args[2] and args[3], "Missing arguments") + local code = args[4] or " " - local value = `{p.Name}:{p.UserId}` + 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 - if newList then - table.insert(newList, value) + local wrapped,scr = Core.NewScript(class,code,false,true) - if Settings.SaveAdmins and levelName and not temp then - TrackTask("Thread: SaveAdmin", Core.DoSave, false, { - Type = "TableAdd"; - Table = {"Settings", "Ranks", levelName, "Users"}; - Value = value - }) - end - end + sb[class][name] = { + Wrapped = wrapped; + Script = scr; + } - 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 + if args[4] then + Functions.Hint(`Created {class} {name} and appended text`, {plr}) + else + Functions.Hint(`Created {class} {name}`, {plr}) 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)) + 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 - - 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 + 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 - end - end - end; - - CheckBan = function(p) - local doCheck = Admin.DoCheck - local banCheck = Admin.DoBanCheck - - 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 - - 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) + 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 + 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 + 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 + 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 - return true, `\n {ban.Reason or "(No reason provided.)"}\n | Banned until {service.FormatTime(ban.EndTime, {WithWrittenDate = true})}` + 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 - 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) + for i, v in sb.LocalScript do + table.insert(tab, {Text = `LocalScript: {i}`, Desc = `Running: {v.Script.Disabled}`}) end + + Remote.MakeGui(plr, "List", {Title = "SB Scripts", Table = tab}) 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") + 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 + }; - 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 + 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 + }; - service.Events.PlayerBanned:Fire(p, reason, doSave, moderator) - 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)") - 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%"; - } + local bytecode = Core.Bytecode(args[2]) + assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`) - table.insert(Core.Variables.TimeBans, value) + 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) - Core.DoSave({ - Type = "TableAdd"; - Table = {"Core", "Variables", "TimeBans"}; - Value = value; - }) + local backpack = targetPlayer:FindFirstChildOfClass("Backpack") + if not backpack then + Functions.Hint(`Couldn't run LocalScript on {playerName} (Backpack missing?)`, {plr}) + return + end - Core.CrossServer("RemovePlayer", p.Name, Variables.BanMessage, value.Reason or "No reason provided") + 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 - 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 + for i, v in service.GetPlayers(plr, args[1]) do + task.spawn(cloneScript, v) end end + }; - service.Events.PlayerBanned:Fire(p, reason, true, moderator) - 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)") - DoBanCheck = function(name: string | number | Instance, check: string | {[string]: any}) - local id = type(name) == "number" and name + local bytecode = Core.Bytecode(args[2]) + assert(string.find(bytecode, "\27Lua"), `LocalScript unable to be created: {string.gsub(bytecode, "Loadstring%.LuaX:%d+:", "")}`) - if type(name) == "userdata" and name:IsA("Player") then - id = name.UserId - name = name.Name + 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 + }; - 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 - 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; - }) - 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 - 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 - 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 - end + Remote.MakeGui(plr,"List",{ + Title = "Starter Scripts"; + Tab = result; + }) + end + }; - local blacklistedCommands = {} - for _, v in container:GetDescendants() do - if v:IsA("TextChatCommand") then - blacklistedCommands[v.PrimaryAlias] = true - blacklistedCommands[v.SecondaryAlias] = true - end + 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!") + + 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 + }; - for name, data in Commands do - local command1, command2 = nil, nil - - if type(data) ~= "table" or data.Hidden then - continue - 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 + 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 - - 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) - - if player then - Process.Command(player, string.sub(text, 2)) - end - end) 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 - 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+)") + 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 + }; - 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 + 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 - 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 + local msg = service.BroadcastFilter(args[3], plr) + assert(msg == args[3], "Message was filtered: "..msg) - if Variables.IsStudio and cmd.NoStudio then - return false, "This command cannot be used in Roblox Studio." - 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 opts.CrossServer and cmd.CrossServerDenied then -- Ignore when disabled then - return false, "This command may not be run across servers (cross-server-blacklisted)." - 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 cmd.CrossServer and not Settings.CrossServerCommands then - return false, "This command has been disabled due to CrossServerCommands being disabled" - 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.IsPlaceOwner(pDat.Player) or adminLevel >= Settings.Ranks.Creators.Level then - return true, nil + 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 + }; - if Admin.IsBlacklisted(pDat.Player) then - return false, "You are blacklisted from running commands." - 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) + 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 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) + 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 + }; BanMenu = { Prefix = Settings.Prefix; @@ -1615,123 +1435,164 @@ return function(Vargs, GetEnv) end, }; - 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 - 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 + }; - 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 + 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 + }; - --// 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 + 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 - 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 + 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 + }; - return tab - 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") - CheckAuthority = function(p, target, actionName, allowSelf) - if p == target then - if allowSelf == false then - Functions.Hint(`You cannot {actionName} yourself`, {p}) - return false + local username = select(2, xpcall(function() + return service.Players:GetNameFromUserIdAsync(userId) + end, function() return args[2] end)) + + 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