Keylogger, the future of textboxes?

I was searching in my desktop for files to delete and found some old projects.

One of those was a project that really wasn’t a project, Keylogger.
What it does> gets keys being pressed, characters being deleted, basically getting user input.

Download
KeyLogger3.rbxm (4.6 KB) :star:
Keylogger2.rbxm (4.5 KB)

Overview
Keylogger 3 is the best one out of all of these. Its the fastest of all of them. It fires if you pasted text, if you copied text, if you typed, if you backspaced, all within like 300 lines. Keylogger 2 can only do half that, and it has 311 lines.

histroy?

yrtext was the original name of this.. project?
take a look if you want to be blinded by how garbage this was

module = script;
local script = {};

do
	if module.Parent and module.Parent:IsA("TextBox") then
		script.TextBox = module.Parent;
	end
	module = nil;
end
do
	repeat wait() until typeof(script.TextBox) == "Instance" and script.TextBox:IsA("TextBox")
	
	function script.isSelecting()
		return script.TextBox.SelectionStart ~= -1;
	end
	script.Text = script.TextBox.Text;
	script.SelectionStart = script.TextBox.SelectionStart
	script.TextBox:GetPropertyChangedSignal("SelectionStart"):Connect(function()
		wait()
		script.SelectionStart = script.TextBox.SelectionStart;
	end)
	function script:GetCursorPosition()
		return self.TextBox.CursorPosition;
	end;
	local prompt = false;
	function script:ChangeText(text)
		if text ~= script.TextBox.Text then
			prompt = true;
		end
		script.TextBox.Text = text;
	end
	function script:SetCursorPosition(number : number)
		self.TextBox.CursorPosition = number;
		return true;
	end
	function script:getSelectedText(CursorPosition, SelectionStart, Text)
		
		if self.isSelecting() then
			local selectionstart = math.min(CursorPosition or self.TextBox.CursorPosition,SelectionStart or self.TextBox.SelectionStart);
			local selectionend = math.max(CursorPosition or self.TextBox.CursorPosition,SelectionStart or self.TextBox.SelectionStart)

			local Text = Text or self.TextBox.Text
			local sub = Text:sub(selectionstart,selectionend-1)
			return sub;
		elseif CursorPosition and SelectionStart and Text then
			if SelectionStart == -1 or CursorPosition == -1 then
				return nil;
			end
			local selectionstart = math.min(CursorPosition or self.TextBox.CursorPosition,SelectionStart or self.TextBox.SelectionStart);
			local selectionend = math.max(CursorPosition or self.TextBox.CursorPosition,SelectionStart or self.TextBox.SelectionStart)

			local Text = Text or self.TextBox.Text
			local sub = Text:sub(selectionstart,selectionend-1)
			return sub;
		
		else
			return nil;
		end
	end
	task.spawn(function()
		local renderstepped = game:GetService("RunService").RenderStepped;
		local uis = game:GetService("UserInputService");
		do
			local event = Instance.new("BindableEvent");
			local event2 = Instance.new("BindableEvent");
			local changed = Instance.new("BindableEvent");
			local cut = Instance.new("BindableEvent");


			local lasttext = script.TextBox.Text;
			local lastcursorposition = script.TextBox.CursorPosition;
			local clearonfocus = false;
			local typing = nil;
			
			uis.InputBegan:Connect(function(key)
				if script.TextBox:IsFocused() then
					if key.UserInputType == Enum.UserInputType.Keyboard then
						typing = key
						repeat renderstepped:Wait() until key.UserInputState == Enum.UserInputState.End;
						typing = nil;
					end
				end
			end)
			script.TextBox.Focused:Connect(function()
				if script.TextBox.ClearTextOnFocus then
					clearonfocus = true;
				end
			end)
			script.TextBox:GetPropertyChangedSignal("CursorPosition"):Connect(function()
				wait()
				lastcursorposition = script.TextBox.CursorPosition;
			end)
			script.TextBox.FocusLost:Connect(function()
				typing = nil;
			end)
			script.TextBox:GetPropertyChangedSignal('Text'):Connect(function()
				if prompt then
					prompt = false;
					return;
				end
				renderstepped:Wait();
				if typing == nil then
					return;
				end
				local added = typing.KeyCode ~= Enum.KeyCode.Backspace
				if added then
					if (uis:IsKeyDown(Enum.KeyCode.LeftControl) or uis:IsKeyDown(Enum.KeyCode.RightControl)) then
						if uis:IsKeyDown(Enum.KeyCode.V) then
							return; -- Paste (CTRL+V)
						end
					end
					local cursorposition = script:GetCursorPosition()
					local sub = script.TextBox.Text:sub(cursorposition-1,cursorposition-1)
					changed:Fire(script.TextBox.Text,lasttext,{
						Character = sub,
						CurrentCursorPosition = cursorposition,
						Typed = added,
						LastSelected = script:getSelectedText(lastcursorposition,script.SelectionStart,lasttext),
						PastCursorPosition = lastcursorposition;
					});
				else
					if (uis:IsKeyDown(Enum.KeyCode.LeftControl) or uis:IsKeyDown(Enum.KeyCode.RightControl)) then
						if uis:IsKeyDown(Enum.KeyCode.X) then
							local cutout = script:getSelectedText(lastcursorposition,script.SelectionStart,lasttext);
							cut:Fire(cutout);
							return; -- Cut (CTRL+X)
						end
					end
					if clearonfocus then
						clearonfocus = false;
						return;
					end
					local sub = lasttext:sub(lastcursorposition-1,lastcursorposition-1)
					local cursorposition = script:GetCursorPosition()
					changed:Fire(script.TextBox.Text,lasttext,{
						Character = sub,
						CurrentCursorPosition = cursorposition,
						Typed = added,
						LastSelected = script:getSelectedText(lastcursorposition,script.SelectionStart,lasttext),
						PastCursorPosition = lastcursorposition;
					});
					event2:Fire(sub,cursorposition,lastcursorposition);
				end
				script.Text = script.TextBox.Text;
				lasttext = script.TextBox.Text;
			end)
			script.Typed = event.Event;
			script.Changed = changed.Event;
			script.Backspace = event2.Event;
			script.Cut = cut.Event;

		end
	end)
end

return script;

If you’re wondering why Keylogger 2 is super formal and big and all that, its a remaster of a remaster of a remaster of yrtext. This had taken months because I kept deleting files.

Keylogger 3, the finale.
I wanted to implement a way to get pasted text and copied text and everything else you could probably do with it (cutting)

original prototype code

local UserInputService = game:GetService("UserInputService");
local TextService = game:GetService("TextService")

local keylogger = {}; -- v.0 prototype

--[[
Keylogger III (pasted from the keylogger 3 latest module)
author : watchamean

Documentation
	inputParameters : {
				Typed : boolean,
				Character : string,
				CursorPosition : number,
				
				LastCursorPosition : number,
				LastSelectionStart : number,
				LastSelected : string?
			}

	keyLogg
		TextBox : TextBox
		KeyPressed : RBXScriptSignal -> (typed : boolean, character : string, inputParameters)
		Changed  : RBXScriptSignal -> (newText : string, lastText : string, inputParameters?)
		--copy doesn't exist yet
		--paste doesn't exist yet
]]--

local assert = assert;
local typeof = typeof;

local Instance_new = Instance.new;

local table_clone = table.clone;
local table_clear = table.clear;
local table_insert = table.insert;

local task_wait = task.wait;
local math_min = math.min;
local math_max = math.max;

function keylogger.new(TextBox)
	assert(typeof(TextBox) == "Instance" and TextBox:IsA("TextBox"), "TextBox must be a TextBox, duh!");
	local self = {};
	local Connections = {};

	local KeyPressed = Instance_new("BindableEvent");
	local Notify = Instance_new("BindableEvent");
	local Changed = Instance_new("BindableEvent");

	local cursorSignalEvent = TextBox:GetPropertyChangedSignal("CursorPosition");
	local lastText = TextBox.Text;

	self.SelectionStart = TextBox.SelectionStart;
	self.CursorPosition = TextBox.CursorPosition;
	self.TextBox = TextBox;
	self.KeyPressed = KeyPressed.Event;
	self.Changed = Changed.Event;
	self.TypedParams = {};

	local pendingChange = nil;

	function self:RegisterParams(typed, character, lastSelected, cursorPos)
		local params = self.TypedParams;
		table_clear(params);

		params.Typed = typed;
		params.Character = character;

		params.CursorPosition = cursorPos;
		params.LastCursorPosition = self.CursorPosition;
		params.LastSelectionStart = self.SelectionStart;
		params.LastSelected = lastSelected;
		params.LastText = lastText;
	end

	function self:GetSelected()
		if TextBox.SelectionStart ~= -1 then
			return TextBox.Text:sub(math_min(TextBox.SelectionStart, TextBox.CursorPosition) - 1, math_max(TextBox.SelectionStart, TextBox.CursorPosition) - 1);
		end
		return nil;
	end

	function self:GetCursorPosition()
		return TextBox.CursorPosition;
	end

	function self:SetCursorPosition(...)
		TextBox.CursorPosition = ...;
	end

	function self:ChangeText(... : string)
		if TextBox.Text ~= ... then
			pendingChange = true;
		end
		TextBox.Text = ...;
	end

	function self:Destroy()
		for i,v in pairs(Connections) do
			task.defer(v.Disconnect, v);
		end
		self.Destroyed = true;
	end


	local function validKeyCode(keyCode : Enum.KeyCode)
		if UserInputService:GetStringForKeyCode(keyCode) ~= "" then
			return 0;
		elseif keyCode.Name:match("Delete") or keyCode == Enum.KeyCode.Backspace then
			return 1;
		elseif keyCode == Enum.KeyCode.Return or keyCode == Enum.KeyCode.KeypadEnter then
			return 2;
		end
		return false;
	end

	local function inputEntered(input)
		local validId = validKeyCode(input.KeyCode);
		if not validId then return end;

		local character;
		local lastSelected;
		local cursorPos = TextBox.CursorPosition;
		local typed = false;

		if self.SelectionStart ~= -1 then
			lastSelected = lastText:sub(math_min(self.SelectionStart, self.CursorPosition) - 1, math_max(self.SelectionStart, self.CursorPosition) - 1);
		end
		if (input.KeyCode.Name == "V" and (UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl))) then
			return
		end

		if validId ~= 1 then
			cursorSignalEvent:Wait();
			cursorPos = TextBox.CursorPosition;
			typed = true;
			character = TextBox.Text:sub(cursorPos - 1, cursorPos - 1);
		elseif lastSelected == nil then
			character = lastText:sub(cursorPos, cursorPos);
		else
			character = lastSelected;
		end

		self:RegisterParams(typed, character, true, cursorPos);
		KeyPressed:Fire(typed, character, table_clone(self.TypedParams));
	end

	local function textChanged()
		if pendingChange then pendingChange = nil; lastText = TextBox.Text;  return end;
		local params = self.TypedParams;

		if params and params.Typed ~= nil then
			params = table_clone(params);
			table_clear(self.TypedParams);
		else
			params = nil;
		end

		Changed:Fire(TextBox.Text, lastText, params);
		task_wait();
		lastText = TextBox.Text; 
	end

	table_insert(Connections, TextBox:GetPropertyChangedSignal("SelectionStart"):Connect(function() task_wait() self.SelectionStart = TextBox.SelectionStart; end));
	table_insert(Connections, TextBox:GetPropertyChangedSignal("CursorPosition"):Connect(function() task_wait() self.CursorPosition = TextBox.CursorPosition; end));
	table_insert(Connections, TextBox:GetPropertyChangedSignal("Text"):Connect(textChanged));

	table_insert(Connections, UserInputService.InputBegan:Connect(function(input)
		if input.UserInputType.Name == "Keyboard" and TextBox:IsFocused() and validKeyCode(input.KeyCode) then
			inputEntered(input);
			local connection; connection = input:GetPropertyChangedSignal("UserInputState"):Connect(function()
				if input.UserInputState.Name == "End" then
					connection:Disconnect();
				else
					inputEntered(input);
				end
			end)
		end
	end))

	return self
end

return keylogger;
2 Likes

Wait, wait, Im kinda confused what this is for exactly, and what uses it has. If it’s in game can’t I just use UIS.InputBegan() and ignore gameProcessed

1 Like

What use cases would this even have? A “Keylogger” as far as I know it’s usually used as some kind of malware…

1 Like

Well for both of you, this was meant for autocompletion/incremental lexing/textbox stuff.

Keylogger 2 was meant for my in-game script editor (which is why it was a double remaster bcuz I kept deleting files). Keylogger 3 was made just because I was bored.

2 Likes

So I really see how Keylogger 2 is helpful, but what is the specific improvements of Keylogger 3, and how much diffirent is it? I’m just still confused

More shorter, it introduces new signals for if when the user pastes or copies text in the textbox. Better organized, memory efficient.
Basically Keylogger 2 with copy and paste.

1 Like