Basic Admin Essentials: copy-and-paste logs tutorial

Hi, this tutorial will guide you on adding a copy-paste logger command for Basic Admin Essentials by thefurryfish / r_r. My system detects copy-pasting by detecting keys that are being pressed if typing. You can use this to check if people are copy-and-pasting anything for things like spamming, or cheating during any kind of interview, or whether someone is using a translator and pasting the output as opposed to being able to speak a language

As far as I know, this can only be done using the LegacyChatService - to switch to this (from creator docs) in the Explorer window, locate TextChatService. Then, in the Properties window, set the ChatVersion property to LegacyChatService.


Tutorial

To do this, we’ll be forking the module for Basic Admin. My way of setting the admin system up is the easiest - make sure that you own both the loader and the module in your inventory, then copy and paste the following into the studio command bar:

loader = game.InsertService:LoadAsset(564796604) config = loader["Basic Admin Essentials 2.0"] config = config["Basic Admin Essentials 2.0"] config.Parent = game.ServerScriptService asset = game.InsertService:LoadAsset(563619835) asset.MainModule.Parent = game.ServerScriptService["Basic Admin Essentials 2.0"] asset:Destroy() loader:Destroy()

Then, modify line 40 of the script that is found in ServerScriptStorage from ['Loader ID'] = 563619835, to ['Loader ID'] = script.MainModule, so that the line now looks like this:

Firstly, add a RemoteEvent into ReplicatedStorage and call it Paste - we’ll use this to transfer the client-based paste detection to the server for logging.
Screenshot 2024-07-13 at 10.52.22

We’ll need to detect when/if the player is pressing ctrl/command/v whilst typing something. In StarterPlayer > StarterPlayerScripts, create a LocalScript and paste the following:

local PlayerGui = game:FindService('Players').LocalPlayer:WaitForChild("PlayerGui")
wait(1)
local ChatBar = PlayerGui.Chat.Frame.ChatBarParentFrame.Frame.BoxFrame.Frame.ChatBar

local userinput = game:GetService('UserInputService')
local replicated = game:GetService("ReplicatedStorage")

local currentlylogging = false
local newtext = ChatBar.Text

userinput.InputBegan:Connect(function(Input)
	if Input.KeyCode == Enum.KeyCode.V and (userinput:IsKeyDown(Enum.KeyCode.LeftControl) or userinput:IsKeyDown(Enum.KeyCode.RightControl)) then
		currentlylogging = true
		wait(0.1)
		currentlylogging = false
	end end)

ChatBar:GetPropertyChangedSignal('Text'):Connect(function()
	if currentlylogging or ((userinput:IsKeyDown(Enum.KeyCode.LeftMeta) or userinput:IsKeyDown(Enum.KeyCode.RightMeta)) and userinput:IsKeyDown(Enum.KeyCode.V))  then
		replicated.Paste:FireServer(string.gsub(ChatBar.Text, newtext, ""))
	end
	newtext = ChatBar.Text
end)

The first part of this script locates the part of the chat system that we need. The third section checks solely if Ctrl and V is being held (for Windows). The fourth section will send the log itself if text was being changed whilst the Windows detection was being done or the Mac Command-button check is true.

Then, we’ll create a server script in ServerScriptService which will add a log to a global table whenever someone pastes something.
Screenshot 2024-07-13 at 10.56.20

_G.pastes = {}
local plrs = game:FindService('Players')
local replicated = game:FindService('ReplicatedStorage')

replicated:WaitForChild("Paste").OnServerEvent:Connect(function(Player, msg)
	table.insert(_G.pastes, 1, Player.Name .. ": " .. msg) 
	if #_G.pastes > 1500 then 
		table.remove(_G.pastes, 1501) 
	end 
end)

I’m pretty sure that Basic Admin has a log limit of 1500, this is reflected in the cmds command so this will insert a log onto the global table whenever the RemoteEvent is fired with the details and will remove the last log when there are over 1500 logs.

You’re free to name both the scripts anything you like since we’re not referencing them anywhere.

Finally, we’ll be adding the command itself into the MainModule. We’ll need to change three parts of the module - the command configuration, the command itself as well as when the list is refreshed. In the MainModule which will be in ServerScriptService > Basic Admin Essentials 2.0, navigate to line 3086.
Screenshot 2024-07-13 at 11.06.47

In like 3086, there will be a long list of commands. Here is the line for chatlogs on 3106, for example.

		{'chatlogs',sysTable.Prefix,Funcs.Display,1,{'chatlogs','','Displays 1500 server chat logs.'}},

The first argument is the command itself, the second is the prefix, the third is where the code for it is located, the fourth is the admin level required for the command, and the fifth is what is shown in cmds. I’m going to copy and paste this line above onto the line below, and modify it as shown:

		{'pastelogs',sysTable.Prefix,Funcs.Display,1,{'pastelogs','','Displays 1500 server paste logs.'}},

You can make the command whatever you like, just make sure to put that wherever I put pastelogs instead. I’ve inserted this new line onto 3107 as shown below:

On line 618, you can see how the code for chatlogs is constructed:

	elseif Command == "chatlogs" then
		local newLogTable = cleanTableData(sysTable.chatLogs,Player)
		if newLogTable then
			local PSA = returnPSAMessage(Player,'all') or returnPSAMessage(Player,'logs')
			remoteEvent:FireClient(Player,'List','Chat Logs',true,true,newLogTable,PSA)
		end

I’m going to copy lines 618-623 and paste it directly below, then modify it as so by deleting the second, third and last line and changing newLogTable to _G.pastes which is our global table for storing the logs.

	elseif Command == "pastelogs" then
		local PSA = returnPSAMessage(Player,'all') or returnPSAMessage(Player,'logs')
		remoteEvent:FireClient(Player,'List','Paste Logs',true,true,_G.pastes,PSA)

The code would look like this:

Now, your game will properly list out paste logs, but the refresh button will not work. In Line 3338, you’ll see the function that runs when the Refresh button is pressed, so we’ll just modify this to send back an updated version of the paste logs when this button is pressed. Under line 3422, adding these two lines will return an updated version of our global table:

				elseif Data[2] == 'Paste Logs' then
					return _G.pastes

The script will now look like this.
Screenshot 2024-07-13 at 11.25.44


Feel free to test this out here by typing :pastelogs after pasting whatever.

1 Like