Creating A Chat Command System Similar to a Discord Bot: Remote Events, :SetCore(), And More!

Intro

Hi there! It’s time for another tutorial! Today we’re going to do some scripting! We’re going to make an admin-style system, with 4 commands. Now, that may not seem like a lot, but I’m going to be teaching you a lot with scripting as we do this. Remote Events, Core GUIs, the Chat Service, all the good stuff. Anyways, let’s get started!

Advanced vs. Beginner

Today’s commands were supposed to extremely simple to make it easier for beginners to understand, but after lots of feedback, I have decided to split the tutorial. There is the less complex, longer code, or there is the more complex, more efficient code. I’ll leave the choice up to you!

Simple

If you want to see the code we are doing today, click below!

Code
ServerScript
game.Players.PlayerAdded:Connect(function(player)
		player.Chatted:Connect(function(message)
			if message == "/kick" then
				game.ReplicatedStorage.KickQuestion1:FireAllClients()
				player.Chatted:Connect(function(message)
					local players = game:GetService("Players")
					local kickedPlayer = players:FindFirstChild(message)
					game.ReplicatedStorage.KickQuestion2:FireAllClients()
					player.Chatted:Connect(function(message)
						kickedPlayer:Kick(message)
						game.ReplicatedStorage.KickSuccess:FireAllClients()
					end)
				end)
			end
			if message == "/speed" then
				game.ReplicatedStorage.SpeedQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local character = player.Character
					local humanoid = character:FindFirstChild("Humanoid")
					humanoid.WalkSpeed = message
					game.ReplicatedStorage.SpeedSuccess:FireAllClients(message)
				end)
			end
			if message == "/kill" then
				game.ReplicatedStorage.KillQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local killPlayer = game.Players:FindFirstChild(message)
					local killCharacter = killPlayer.Character
					local human = killCharacter:FindFirstChild("Humanoid")
					human.Health = 0
					game.ReplicatedStorage.KillSuccess:FireAllClients()
				end)
			end
			if message == "/teleport" then
				game.ReplicatedStorage.TeleportQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local character = player.Character
					local HRP = character:FindFirstChild("HumanoidRootPart")
					HRP.CFrame = CFrame.new(Vector3.new(message))
					game.ReplicatedStorage.TeleportSuccess:FireAllClients()
			end)
		end
	end)
end)
LocalScript in StarterPlayerScripts
game.ReplicatedStorage.KickQuestion1.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Who would you like to kick?"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KickQuestion2.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "What were they doing?"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KickSuccess.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully Kicked!"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.SpeedQuestion.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "What walkspeed do you want?"; 
				Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.SpeedSuccess.OnClientEvent:Connect(function(Event)
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully changed walkspeed to "..Event;
				Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KillQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Who would you like to kill?";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KillSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully killed player!";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.TeleportQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Where would you like to go?";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.TeleportSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully teleported!";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

Setup

To make this system, you’ll need two scripts, and a lot of remote events. Add one Server Script into ServerScriptService, mine will be named ChatHandler. Add a Local Script into StarterPlayerScripts, which can be found within StarterPlayer. Now, for the events. We will need them all in ReplicatedStorage. Here is a screenshot of all of them.
Screen Shot 2020-10-03 at 8.15.20 AM
These will all be used in a command, believe it or not. Well, that’s it for setup! Let’s actually start scripting!

Scripting

Before we just jump into some advanced commands, let’s set up our script with a few simple lines of code:

game.Players.PlayerAdded:Connect(function(player)
--All new code will be found in here
end)

This is just a simple code to get the “local player” in a ServerScript. When the Remote Event, PlayerAdded, is fired, this script will connect it to a function, with the parameter of player.
Now, we need the player to chat in order for actual commands to run, so let’s add another function.

game.Players.PlayerAdded:Connect(function(player)
--All new code will be found in here
player.Chatted:Connect(function(message)
--We will now check what "message" is
end
end)

Okay, this new addition basically connects the player's Chatted event to a function, with the parameter message, which we can now check what it is. Let’s add four different if statements to the code.

game.Players.PlayerAdded:Connect(function(player)
--All new code will be found in here
player.Chatted:Connect(function(message)
--We will now check what "message" is
if message == "/kick" then
--kick command here
end

if message == "/kill" then
--kill command here
end

if message == "/speed" then
--speed command here
end

if message == "/teleport" then
--teleport command here
end
       end
   end)

So we just added 4 options for message to be. It can be typed as anything, but the script will only run further if it is either /kick, /kill, /speed, or /teleport. So, our script is now setup for the commands, now let’s start scripting them!

/Kick

This is a pretty common command, used commonly for moderation systems. We will have ours work similar to a discord bot, in the fact that the system will respond with questions. Let’s start adding code to the first if statement.

if message == "/kick" then -- The old if statement from earlier
game.ReplicatedStorage.KickQuestion1:FireAllClients()
end

What we are doing here is fairly simple. We are firing that Remote Event, KickQuestion1 to ALL of the clients. This will show up in all of the clients’ chats. So, we of course need to make that event do something, so let’s add the following code to our ChatSender script.

game.ReplicatedStorage.KickQuestion1.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Who would you like to kick?"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

What this code is doing, is using a function called SetCore. That is a function that can set CoreGUIs, such as the Chat in this case, or things like the TopBar. This script is PCalling a function, for more info on pcalls, you can click on this awesome tutorial by @ReturnedTrue. We are pcalling a function that sets a core GUI, in this case a chat message. We are doing it with a table with the info needed for the message. There is Text, Color, Font, and TextSize. All needed for a chat message. Now, let’s get back to our ChatHandler script!

if message == "/kick" then -- The old if statement from earlier
game.ReplicatedStorage.KickQuestion1:FireAllClients()
  player.Chatted:Connect(function(message)
    local kickedplayer = game.Players:FindFirstChild(message)
    game.ReplicatedStorage.KickQuestion2:FireAllClients()
    end
end

Here, we are having the player chat again, and then connecting it to the parameter message. We are then finding the first child of the Players service, who will be kicked. Now, we have one more remote event to handle.

game.ReplicatedStorage.KickQuestion2.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "What were they doing?"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

This script is pretty much the same, except for two key elements. We are instead handling the event KickQuestion2 instead of KickQuestion1, and are using a different text, What were they doing? This is going to come in handy, as when you kick a player, you can use a reason within the parentheses. For example,

player:Kick("STOP IT!")

It would then show that they were disconnected from the game, with the text, STOP IT! Now, let’s get back to the commands.

if message == "/kick" then -- The old if statement from earlier
game.ReplicatedStorage.KickQuestion1:FireAllClients()
  player.Chatted:Connect(function(message)
    local kickedplayer = game.Players:FindFirstChild(message)
    game.ReplicatedStorage.KickQuestion2:FireAllClients()
     player.Chatted:Connect(function(message)
      kickedplayer:Kick(message)
       game.ReplicatedStorage.KickSuccess:FireAllClients()
       end)
    end
end

You’ll notice here, we have the player chat Again Yes, you read that right, again. We need to get the message again, then we need to actually Kick() the player. Notice, within the parentheses, we have message. This means that whatever the player messaged last, that will be what they are kicked for. Now, we have one more event to handle. It’s called KickSuccess. I’m going to challenge you to write the code for this one. We want it to send the message, Successfully kicked player!

Solution
game.ReplicatedStorage.KickSuccess.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully Kicked player!"; 
				Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

Now that we have that working, let’s move onto the next command!

/Kill

This is another command commonly used with admin systems, and it kills whoever you’d like! Let’s start by firing the KillQuestion Remote Event.

if message == "/kill" then
	game.ReplicatedStorage.KillQuestion:FireAllClients()			
end

Now of course, we need to handle said event, so going back to the ChatSender script:

game.ReplicatedStorage.KillQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Who would you like to kill?";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

You’ll notice a few things here, the text is different, being “Who would you like to kill?”. The color is also different, it’s red this time. I’m going to use a different color for each command. Now, we need further code in the ChatHandler.

if message == "/kill" then
				game.ReplicatedStorage.KillQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local killPlayer = game.Players:FindFirstChild(message)
					local killCharacter = killPlayer.Character
					local human = killCharacter:FindFirstChild("Humanoid")
					human.Health = 0
					game.ReplicatedStorage.KillSuccess:FireAllClients()
				end)
			end

Let’s walk through this code. We are getting the player to chat again, and getting the message. The next few lines are getting the player that will be killed. The killplayer is the actual player in game.Players. It’s killCharacter is the model of the player in the workspace, and the human is the Humanoid within that. We then set the health to 0. You’ll notice we are firing another remote event, so we of course need to handle that.

game.ReplicatedStorage.KillSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully killed player!";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

We are really just sending a success message in the chat. Everything but the Text is the same. Now we need to make the /speed command! Let’s add some code into another if statement.

if message == /speed then
game.ReplicatedStorage.SpeedQuestion:FireAllClients()
end

This code is pretty self explanatory. We are just firing a remote event. Let’s now make that event do something.

game.ReplicatedStorage.SpeedQuestion.OnClientEvent:Connect(function()
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "What walkspeed do you want?"; 
				Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

This code is fairly similar to the other ones, we just have a few different things. We are of course handling a different event, and changing the text. The text color is also different, it’s blue this time. After we send this question, we need to have the player chat again.

if message == "/speed" then
				game.ReplicatedStorage.SpeedQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local character = player.Character
					local humanoid = character:FindFirstChild("Humanoid")
					humanoid.WalkSpeed = message
					game.ReplicatedStorage.SpeedSuccess:FireAllClients(message)
				end)
			end

The new code here is again having the player chat, then finding their character through player.Character. Then we need to find the Humanoid of the character, and change the walkspeed to message. Notice we are firing the remote event with the parameter message this time. That can be found by special handling. Look at this code:

game.ReplicatedStorage.SpeedSuccess.OnClientEvent:Connect(function(Event)
	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully changed walkspeed to "..Event;
				Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

Notice how we are connecting the function with the parameter Event. This is going to get the message we fired it with. We use Event in the text to show what the speed was changed to. After you type a string value, saying ..something will add in a variable. The rest of the code is the same. Let’s move on to the teleport command!

/Teleport

We’ve reached the final command! It’s time to teleport people! We need to start by making sure we have the teleport events, again, shown here:
Screen Shot 2020-10-03 at 8.15.20 AM
Now we need to start adding code! Let’s start in the ChatHandler script.

if message == "/teleport" then
  game.ReplicatedStorage.TeleportQuestion:FireAllClients()
    end	

All we are doing here is simply firing the remote event. We of course now have to handle it in ChatSender.

game.ReplicatedStorage.TeleportQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Where would you like to go?";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

This script is pretty much the same, the ScreenGui variable is different because I got lazy and didn’t want to type the whole thing. The color this time is pink, and the text is asking where the player wants to go. Now we need to go back to our original script.

if message == "/teleport" then
	game.ReplicatedStorage.TeleportQuestion:FireAllClients()
		player.Chatted:Connect(function(message)
			local character = player.Character
			local HRP = character:FindFirstChild("HumanoidRootPart")
				HRP.CFrame = CFrame.new(Vector3.new(message))
				game.ReplicatedStorage.TeleportSuccess:FireAllClients()
			end)

So what we are doing here is having the player chat yet again, and then defining a few things. We are getting the player’s character, then finding the HumanoidRootPart, or in this script, HRP. The way to successfully change a player’s position is through their HumanoidRootPart. We do have to change the CFrame instead of Position. We are changing the CFrame with a Vector3, which is the message in this case. Finally, we fire a remote event. Handling is quite simple.

game.ReplicatedStorage.TeleportSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully teleported!";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end) 

We have the same code, with the Text “Successfully teleported!”. Anyways, here’s the full code again for those of you following along.

ServerScript
game.Players.PlayerAdded:Connect(function(player)
		player.Chatted:Connect(function(message)
			if message == "/kick" then
				game.ReplicatedStorage.KickQuestion1:FireAllClients()
				player.Chatted:Connect(function(message)
					local players = game:GetService("Players")
					local kickedPlayer = players:FindFirstChild(message)
					game.ReplicatedStorage.KickQuestion2:FireAllClients()
					player.Chatted:Connect(function(message)
						kickedPlayer:Kick(message)
						game.ReplicatedStorage.KickSuccess:FireAllClients()
					end)
				end)
			end
			if message == "/speed" then
				game.ReplicatedStorage.SpeedQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local character = player.Character
					local humanoid = character:FindFirstChild("Humanoid")
					humanoid.WalkSpeed = message
					game.ReplicatedStorage.SpeedSuccess:FireAllClients(message)
				end)
			end
			if message == "/kill" then
				game.ReplicatedStorage.KillQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local killPlayer = game.Players:FindFirstChild(message)
					local killCharacter = killPlayer.Character
					local human = killCharacter:FindFirstChild("Humanoid")
					human.Health = 0
					game.ReplicatedStorage.KillSuccess:FireAllClients()
				end)
			end
			if message == "/teleport" then
				game.ReplicatedStorage.TeleportQuestion:FireAllClients()
				player.Chatted:Connect(function(message)
					local character = player.Character
					local HRP = character:FindFirstChild("HumanoidRootPart")
					HRP.CFrame = CFrame.new(Vector3.new(message))
					game.ReplicatedStorage.TeleportSuccess:FireAllClients()
			       end)
			end
		end)	
	end
end)

LocalScript in StarterPlayerScripts

game.ReplicatedStorage.KickQuestion1.OnClientEvent:Connect(function()
local StarterGui = game:GetService(“StarterGui”)
repeat
wait()
local Success = pcall(function()
StarterGui:SetCore(“ChatMakeSystemMessage”, {
Text = “Who would you like to kick?”;
Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
TextSize = 18 --Text size, defaults to 18
})
end)
until Success
end)

game.ReplicatedStorage.KickQuestion2.OnClientEvent:Connect(function()
local StarterGui = game:GetService(“StarterGui”)
repeat
wait()
local Success = pcall(function()
StarterGui:SetCore(“ChatMakeSystemMessage”, {
Text = “What were they doing?”;
Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
TextSize = 18 --Text size, defaults to 18
})
end)
until Success
end)

game.ReplicatedStorage.KickSuccess.OnClientEvent:Connect(function()
local StarterGui = game:GetService(“StarterGui”)
repeat
wait()
local Success = pcall(function()
StarterGui:SetCore(“ChatMakeSystemMessage”, {
Text = “Successfully Kicked!”;
Color = Color3.fromRGB(255, 255, 255); --Chat message color, defaults to white
Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
TextSize = 18 --Text size, defaults to 18
})
end)
until Success
end)

game.ReplicatedStorage.SpeedQuestion.OnClientEvent:Connect(function()
local StarterGui = game:GetService(“StarterGui”)
repeat
wait()
local Success = pcall(function()
StarterGui:SetCore(“ChatMakeSystemMessage”, {
Text = “What walkspeed do you want?”;
Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
TextSize = 18 --Text size, defaults to 18
})
end)
until Success
end)

game.ReplicatedStorage.SpeedSuccess.OnClientEvent:Connect(function(Event)

	local StarterGui = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully changed walkspeed to "..Event;
				Color = Color3.fromRGB(0, 255, 255); --Chat message color, defaults to white
				Font = Enum.Font.SourceSansBold; --Chat message font, defaults to SourceSansBold
				TextSize = 18 --Text size, defaults to 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KillQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Who would you like to kill?";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.KillSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully killed player!";
				Color = Color3.fromRGB(255, 0, 0);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.TeleportQuestion.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Where would you like to go?";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)

game.ReplicatedStorage.TeleportSuccess.OnClientEvent:Connect(function()
	local SG = game:GetService("StarterGui")
	repeat
		wait()
		local Success = pcall(function()
			SG:SetCore("ChatMakeSystemMessage", {
				Text = "Successfully teleported!";
				Color = Color3.fromRGB(255, 0, 255);
				Font = Enum.Font.SourceSansBold;
				TextSize = 18
			})
		end)
	until Success
end)
Advanced

If you want to view the full code we’re making today, click below!

Code
ServerScript
game.Players.PlayerAdded:Connect(function(player)
	local admins = {
		"punishertheawesome";
		"YourUserName";
		"AnotherUserName"
	}
	if player.Name == admins[1] or admins[2] or admins[3] then
		local messages = {
			"/kick";
			"/kill";
			"/speed";
			"/teleport"
		}
		player.Chatted:Connect(function(message)
			if message == messages[1] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("KickQ1")
				player.Chatted:Connect(function(message)
					local kickedPlayer = game.Players:FindFirstChild(message)
					game.ReplicatedStorage.ChatEvent:FireAllClients("KickQ2")
					player.Chatted:Connect(function(message)
						kickedPlayer:Kick(message)
						game.ReplicatedStorage.ChatEvent:FireAllClients("KickSuccess")
					end)
				end)
			end
			if message == messages[3] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("SpeedQ")
				player.Chatted:Connect(function(message)
					local character = player.Character
					local humanoid = character:FindFirstChild("Humanoid")
					humanoid.WalkSpeed = message
					game.ReplicatedStorage.ChatEvent:FireAllClients("SpeedSuccess")
				end)
			end
			if message == messages[2] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("KillQ")
				player.Chatted:Connect(function(message)
					local killedPlayer = game.Players:FindFirstChild(message)
					local killCharacter = killedPlayer.Character
					local humanoid = killCharacter:FindFirstChild("Humanoid")
					humanoid.Health = 0
					game.ReplicatedStorage.ChatEvent:FireAllClients("KillSuccess")
				end)
			end
			if message == messages[4] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("TeleQ")
				player.Chatted:Connect(function(message)
					local character = player.Character
					local HRP = character:FindFirstChild("HumanoidRootPart")
					HRP.CFrame = CFrame.new(Vector3.new(message))
					game.ReplicatedStorage.ChatEvent:FireAllClients("TeleSuccess")
			end
		end)
	end
end)
LocalScript in StarterPlayerScripts
game.ReplicatedStorage.ChatEvent.OnClientEvent:Connect(function(Event)
	local eventNames = {
		"KickQ1";
		"KickQ2";
		"KickSuccess";
		"SpeedQ";
		"SpeedSuccess";
		"KillQ";
		"KillSuccess";
		"TeleQ";
		"TeleSuccess"
	}
	if Event == eventNames[1] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Who would you like to kick?";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[2] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "What were they doing?";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[3] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully kicked player!";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[4] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "What walkspeed do you want?";
					Color = Color3.fromRGB(255, 0, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[5] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully changed walkspeed!";
					Color = Color3.fromRGB(255, 0, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[6] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Who would you like to kill?";
					Color = Color3.fromRGB(255, 0, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[7] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully killed player!";
					Color = Color3.fromRGB(255, 0, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[8] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Where would you like to go?";
					Color = Color3.fromRGB(255, 255, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[9] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully teleported!";
					Color = Color3.fromRGB(255, 255, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	end
end)

Setup

The setup for this system is a lot simpler, as we only need one RemoteEvent. I’m calling mine ChatEvent. I am also going to explain this one a little less, as the simple version has a lot of explanation in it. I will only go in depth with the new stuff in this version. Let’s get started by getting the player, and making an admin table.

game.Players.PlayerAdded:Connect(function(player)
 local admins = {
"punishertheawesome";
"Officiall_Studios";
"YourUserName";
}
end)

All we are doing here is getting the player with function(player), and adding a table of admin usernames. Let’s now check if the player is any one of these.

game.Players.PlayerAdded:Connect(function(player)
 local admins = {
"punishertheawesome";
"Officiall_Studios";
"YourUserName";
}
if player.Name == admins[1] or admins[2] or admins[3] then
player.Chatted:Connect(function(message)
  end)
 end
end)

We are checking if the player is any number of the table, so admins[1] would be “punishertheawesome”, and admins[2] would be “Officiall_Studios”, and so on and so forth. We are then having the player chat, and getting the message. Let’s now add a table with the message commands.

game.Players.PlayerAdded:Connect(function(player)
 local admins = {
"punishertheawesome";
"Officiall_Studios";
"YourUserName"
}
if player.Name == admins[1] or admins[2] or admins[3] then
player.Chatted:Connect(function(message)
local messages = {
"/kick";
"/kill";
"/speed";
"/teleport"
}
if message == messages[1] then
end
if message == messages[2] then
end
if message == messages[3] then
end
if message == messages[4] then
end
  end)
 end
end)

So here we have a table with the messages, then 4 if statements to check what the message is with each string in the table. Now it’s time to start filling in functions! Let’s start with /kill.

if message == messages[1] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("KickQ1")
				player.Chatted:Connect(function(message)
					local kickedPlayer = game.Players:FindFirstChild(message)
					game.ReplicatedStorage.ChatEvent:FireAllClients("KickQ2")
					player.Chatted:Connect(function(message)
						kickedPlayer:Kick(message)
						game.ReplicatedStorage.ChatEvent:FireAllClients("KickSuccess")
					end)
				end)

Let’s briefly go over this code. First off we are firing our ChatEvent, with the parameter KickQ1. This can be handled in the LocalScript, which we will cover in a minute. All this does is make a chat message asking who needs to be kicked. Then we have the player chat again, and define the player being kicked by finding who the message is. We are searching in Players. After that, we are firing the event with “KickQ2”. We are going to get these parameters in the ChatSender script.

game.ReplicatedStorage.ChatEvent.OnClientEvent:Connect(function(Event)
	local eventNames = {
		"KickQ1";
		"KickQ2";
		"KickSuccess";
		"SpeedQ";
		"SpeedSuccess";
		"KillQ";
		"KillSuccess";
		"TeleQ";
		"TeleSuccess"
	}
	if Event == eventNames[1] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Who would you like to kick?";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[2] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "What were they doing?";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end

Okay, I know this is a lot of code, but let’s break it down a little bit. We need to first of all connect the remote event to a function, then we need to get Event, which is the text we fire it with. Our table is all of the things we will eventually fire it with, then we check if it is a number of that table. We then are setting a CoreGui, which are things like the topbar, or the chat in this case. We are setting it with a table of all the things needed, such as the text and the font and the color. We do this twice for the two remote events we fired. Let’s go back to the original script for a second. We are kicking the player with the message, which basically just tells the player what the reason for being kicked was. Now for the next function.

if message == messages[2] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("KillQ")
				player.Chatted:Connect(function(message)
					local killedPlayer = game.Players:FindFirstChild(message)
					local killCharacter = killedPlayer.Character
					local humanoid = killCharacter:FindFirstChild("Humanoid")
					humanoid.Health = 0
					game.ReplicatedStorage.ChatEvent:FireAllClients("KillSuccess")
				end)
			end

This code is checking if the message is the 2nd item of the table, which is /kill. We fire the event with the parameter KillQ, and then find the Humanoid that will be killed with a long string of :FindFirstChild. We change Humanoid's health to 0, then fire the event with KillSuccess. Let’s now handle the event the two times we fired it.

if Event == eventNames[3] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully kicked player!";
					Color = Color3.fromRGB(255, 255, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[4] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "What walkspeed do you want?";
					Color = Color3.fromRGB(255, 0, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end

Here we are doing everything the same as last time, except for a few things. We are getting different strings of the table, eventNames[5] and eventNames[6]. (The table is out of order) We end up changing the text to ask who needs to be killed, and to print a success. The color is also different now, but it doesn’t have to be. Now we can do the next function!

if message == messages[3] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("SpeedQ")
				player.Chatted:Connect(function(message)
					local character = player.Character
					local humanoid = character:FindFirstChild("Humanoid")
					humanoid.WalkSpeed = message
					game.ReplicatedStorage.ChatEvent:FireAllClients("SpeedSuccess")
				end)
			end

This code is similar to our last one, we are checking if the message is a number in the statement, in this case [3] which makes /speed. We are then firing a remote event with SpeedQ, which we will handle later. After this we get the player’s next Chatted event. Next we find the Character of the player, then the Humanoid, then change it’s ``WalkSpeedto themessage. We are firing the remote event again with SpeedSuccess`. Let’s now handle these two events.

if Event == eventNames[4] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "What walkspeed do you want?";
					Color = Color3.fromRGB(255, 0, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[5] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully changed walkspeed!";
					Color = Color3.fromRGB(255, 0, 255);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end

These are also the exact same. The only difference is we are handling different parameters, sending different Text, and using a different Color. Now we can move onto the final command!

if message == messages[4] then
				game.ReplicatedStorage.ChatEvent:FireAllClients("TeleQ")
				player.Chatted:Connect(function(message)
					local character = player.Character
					local HRP = character:FindFirstChild("HumanoidRootPart")
					HRP.CFrame = CFrame.new(Vector3.new(message))
					game.ReplicatedStorage.ChatEvent:FireAllClients("TeleSuccess")
			end
		end)
	end
end)

This code is slightly different than the other code. We are still checking the message, and getting the player’s Character, but that’s where it ends. We start by firing the remote event with TeleQ, then we need the player to chat again. We get the Character, then find a new part. We are finding the HumanoidRootPart of the player. This is what we move to teleport a player. After that we change the CFrame of the HumanoidRootPart, because that’s the way of safely teleporting a Humanoid. Changing Position will kill them. The CFrame is a Vector3, which is made of message, allowing you to choose where you go. Now we just have to handle the two events we fired!

if Event == eventNames[8] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Where would you like to go?";
					Color = Color3.fromRGB(255, 255, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	if Event == eventNames[9] then
		local SG = game:GetService("StarterGui")
		repeat
			wait()
			local Success = pcall(function()
				SG:SetCore("ChatMakeSystemMessage", {
					Text = "Successfully teleported!";
					Color = Color3.fromRGB(255, 255, 0);
					Font = Enum.Font.SourceSansBold;
					TextSize = 18
				})
			end)
		until Success
	end
	end
end)

You know the drill. All we are doing here is sending two chat messages, with the last items of the table. The color and text are different, but nothing else is. Anyways, congratulations! You’ve made a chat system!

Outro

Well, that’s it for today! Thank you for reading! I hope you not only learned how to make chat commands, but about remote events and how to use them! If you want to view the video version, click here! If you want to see them all, click here! If you have any questions, feel free to reply! Thanks for reading! Have a good day!

29 Likes

Very Nice. I’ll take it lol :slight_smile:

3 Likes

I have some feedbacks on your tutorial.

This worried me a lot, you could’ve just add a folder and store all of the remote events in there.

This is inefficient. If your storing a list of admin commands, considering storing them in a table, NOT JUST using the chatted event and elseif statements. Your code will be very long. You can refer to AlvinBlox’s Making Admin Commands Tutorial Video on YouTube.

The rest of the post is all about the functions of each command, don’t store them in the chatted event but rather storing as a function in the table.

Overall, your tutorial is really inefficient. I’m sorry to say that.

4 Likes

Intriguing tutorial! I already know how to make chat commands myself, but I always like to stroll around a good resource talking about working with chats.

I haven’t completely read your tutorial, but I did notice early on that you’re working with the Chatted event of players. For any chat work going forward, I’d recommend that you tailor your work towards the Lua Chat System. Chatted still works for any work you need to do with detecting when player chats, but the Lua Chat System’s extensibility lets you do even more with chats.

I should also point out that the Lua Chat system has a built-in way of making chat commands, so you hardly have to do any of the work yourself. You just need to know how to set up the command module, where to put it and the code to make the actual command work. The rest is handled for you.

5 Likes

This is a tutorial that could be useful but has many flaws.

It uses WAY too many remotes, it should be only one remote with an argument for a message.

It sends the message to everybody in the server and could confuse people.

The time it takes to run a command on someone would take too long; especially if the admin is trying to hurry.

Everybody can see who their running commands on, making this not only very visible, spams chat, but can also hurt somebody as the person who is being kicked or banned could spread rumors about the admin, as they can tell who they are being ran commands are.

Clearly, anybody can run commands and this really ruins this whole tutorial as there’s no visible whitelist, its super easy to add one!!

If you could, please revise your tutorial, it isn’t very practical for real use.

4 Likes

Thank you for your feedback! I was trying to make this tutorial as simple as possible, so I wanted to not restrict this, as a lot of beginners have a hard time figuring out what to do with the player to make them eligible. I will try and add a restricted version, though!

1 Like

Great tutorial, just one suggestion.

Roblox sometimes breaks their chat system and add a special character at the end of all chatted messages, that special character being “\r”

What I’ve been doing ever since that begun is using a string.gsub() operation to remove the \r if it’s found. Might sound a bit silly but if Roblox decides to get funny with their chat again the entire script can break and games that heavily rely on moderation can be defenseless while they try to think of a solution.

Practical Use:

game.Players.PlayerAdded:Connect(function(player)
   player.Chatted:Connect(function(message)

   local newMessage = string.gsub(message, "\r", "") -- Use this instead of "message"

   end)
end)

Thank you for the suggestion! I do not know if this is happening currently, but what if the character was different? I have seen it happen with different characters, like ’ turning into ? a while ago.

Thank you for telling me this! This would definitely be a cool thing to check out! The reason I’m doing it like this is because I wanted it to work similar to a Discord bot, which I have now added into the title. I may try and do something with this in the future, though! To @ItzMeZeus_IGotHacked and @infiniteRaymond, This tutorial was supposed to be the bare-bones beginner stuff, so I didn’t want to complicate things too much. I have kept the original script in the Simple version, but have added a more efficient version in Advanced!

1 Like

To add on to this, it is probably a good idea to store commands as functions in a table and check whether a specific command matches an index in the table:

local commands = {}

commands.kick = function(player, args)
     -- command logic here
end

local function handleCommand(playerName, message)
     -- not going to go over string matching
     
     local commandName = 'kick'
     local args = ['TheGaelicKing', 'This is a test']

     if (commands[commandName]) then
          commands[commandName](args)
          -- return true to hide the message
          return true
     end

     -- return false because there was no match
     return false
end

-- run function (required by chat modules if you decide to take this approach)
local function run(chatService)
     chatService:RegisterProcessCommandsFunction('handleCommand', handleCommand)
end

return run

I personally like to use chat modules that act as a front-end to the command module while the actual command logic exists separately in ServerScriptService.

1 Like

Thank you for telling me this! This honestly looks a little complicated to me, but I’ll look into it!

This way is bad practice cuz it wasting lot of time.

There is error while comparing.

You must do this:

if player.Name == admins[1] or player.Name == admins[2] or player.Name == admins[3] then
-- This is wasting of time adding too many "if" statements and it's bad practice.
     end

or

local V_IsAdmin = false
   for i, v in pairs(admins) do
         if player.Name == v then
                 V_IsAdmin = true -- player is admin
          end
   end
-- this method is good practice as you don't need add too many "if" statements.
-- after loop thought admins
  if V_IsAdmin == false then return end; -- if it's not a admin then we denied him for using commands

Looping is always your best friend.

1 Like

Thank you for telling me this! I will change the code. For this tutorial, I tried to stay away from loops just to keep it as simple as possible.

and for reasons like these, I do not recommend people using chat based admin commands, in lieu of a custom admin GUI, external admin services, or the awesome Cmdr.

1 Like

I am aware of this. This particular tutorial is supposed to make it similar to a discord server.

Thank you to everyone who has read so far! I really appreciate it! I have now published a YouTube video for the advanced version of the tutorial! https://youtu.be/WnER-MjSEV4

you mean discordia but robloxia?

1 Like

I guess so! I hadn’t thought of that! I think that’s already a Roblox game, though.

1 Like

i did remake discord.js but it is roblox.lua, click here to go to my module. theres an example and a list of functions, sorry if it is messy because i dont know how to make it shorter :flushed:. any way dont touch the folders / module in it, because it automaticly construct

2 Likes