-- ChatLogger.exe - Logs all chat messages in a GUI local Players = game:GetService("Players") local StarterGui = game:GetService("StarterGui") local TextService = game:GetService("TextService") local player = Players.LocalPlayer -- Global variables for GUI state local showTimestamps = false local showDisplayName = false local messageHistory = {} -- Store original message data local maxHistory = 500 -- maximum number of stored messages local currentSearch = "" -- current search filter (lowercase) -- Create the ChatLogger GUI local function createChatLoggerGUI() -- Create the main ScreenGui local screenGui = Instance.new("ScreenGui") screenGui.Name = "ChatLogger" screenGui.ResetOnSpawn = false screenGui.Parent = player:WaitForChild("PlayerGui") -- Create the main frame local mainFrame = Instance.new("Frame") mainFrame.Name = "MainFrame" mainFrame.Size = UDim2.new(0, 400, 0, 300) mainFrame.Position = UDim2.new(0, 10, 0, 10) mainFrame.BackgroundColor3 = Color3.fromRGB(45, 45, 45) mainFrame.BorderSizePixel = 0 mainFrame.Active = true mainFrame.Draggable = true mainFrame.Parent = screenGui -- Rounded corners and padding for main frame local mainCorner = Instance.new("UICorner") mainCorner.CornerRadius = UDim.new(0, 8) mainCorner.Parent = mainFrame local mainPadding = Instance.new("UIPadding") mainPadding.PaddingTop = UDim.new(0, 6) mainPadding.PaddingLeft = UDim.new(0, 6) mainPadding.PaddingRight = UDim.new(0, 6) mainPadding.PaddingBottom = UDim.new(0, 6) mainPadding.Parent = mainFrame -- Create the title bar local titleBar = Instance.new("Frame") titleBar.Name = "TitleBar" titleBar.Size = UDim2.new(1, 0, 0, 30) titleBar.Position = UDim2.new(0, 0, 0, 0) titleBar.BackgroundColor3 = Color3.fromRGB(30, 30, 30) titleBar.BorderSizePixel = 0 titleBar.Parent = mainFrame -- Title bar styling local titleCorner = Instance.new("UICorner") titleCorner.CornerRadius = UDim.new(0, 8) titleCorner.Parent = titleBar local titleGradient = Instance.new("UIGradient") titleGradient.Color = ColorSequence.new({ ColorSequenceKeypoint.new(0, Color3.fromRGB(40, 40, 40)), ColorSequenceKeypoint.new(1, Color3.fromRGB(28, 28, 28)), }) titleGradient.Parent = titleBar local titleStroke = Instance.new("UIStroke") titleStroke.Color = Color3.fromRGB(0, 0, 0) titleStroke.Transparency = 0.7 titleStroke.Parent = titleBar -- Helper: consistent button styling + hover local function styleButton(btn, baseColor, hoverColor) local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 6) corner.Parent = btn btn.BackgroundColor3 = baseColor btn.MouseEnter:Connect(function() btn.BackgroundColor3 = hoverColor end) btn.MouseLeave:Connect(function() btn.BackgroundColor3 = baseColor end) end -- Create the title label local titleLabel = Instance.new("TextLabel") titleLabel.Name = "TitleLabel" titleLabel.Size = UDim2.new(1, -280, 1, 0) titleLabel.Position = UDim2.new(0, 0, 0, 0) titleLabel.BackgroundTransparency = 1 titleLabel.Text = "ChatLogger.exe" titleLabel.TextColor3 = Color3.fromRGB(255, 255, 255) titleLabel.TextScaled = false titleLabel.TextSize = 18 titleLabel.Font = Enum.Font.Code titleLabel.TextXAlignment = Enum.TextXAlignment.Left titleLabel.TextWrapped = false titleLabel.Parent = titleBar -- Create Close button local closeButton = Instance.new("TextButton") closeButton.Name = "CloseButton" closeButton.Size = UDim2.new(0, 36, 0, 24) closeButton.Position = UDim2.new(1, -40, 0, 3) closeButton.BackgroundColor3 = Color3.fromRGB(200, 50, 50) closeButton.BorderSizePixel = 0 closeButton.Text = "X" closeButton.TextColor3 = Color3.fromRGB(255, 255, 255) closeButton.TextScaled = false closeButton.TextSize = 16 closeButton.Font = Enum.Font.SourceSansBold closeButton.Parent = titleBar styleButton(closeButton, Color3.fromRGB(200,50,50), Color3.fromRGB(230,80,80)) -- Create Refresh button local refreshButton = Instance.new("TextButton") refreshButton.Name = "RefreshButton" refreshButton.Size = UDim2.new(0, 42, 0, 24) refreshButton.Position = UDim2.new(1, -86, 0, 3) refreshButton.BackgroundColor3 = Color3.fromRGB(50, 150, 200) refreshButton.BorderSizePixel = 0 refreshButton.Text = "R" refreshButton.TextColor3 = Color3.fromRGB(255, 255, 255) refreshButton.TextScaled = false refreshButton.TextSize = 16 refreshButton.Font = Enum.Font.SourceSansBold refreshButton.Parent = titleBar styleButton(refreshButton, Color3.fromRGB(50,150,200), Color3.fromRGB(80,180,220)) -- Create Timestamp toggle button local timestampButton = Instance.new("TextButton") timestampButton.Name = "TimestampButton" timestampButton.Size = UDim2.new(0, 40, 0, 24) timestampButton.Position = UDim2.new(1, -128, 0, 3) timestampButton.BackgroundColor3 = Color3.fromRGB(100, 100, 100) timestampButton.BorderSizePixel = 0 timestampButton.Text = "TS" timestampButton.TextColor3 = Color3.fromRGB(255, 255, 255) timestampButton.TextScaled = false timestampButton.TextSize = 14 timestampButton.Font = Enum.Font.SourceSansBold timestampButton.Parent = titleBar styleButton(timestampButton, Color3.fromRGB(100,100,100), Color3.fromRGB(140,140,140)) -- Create Display Name toggle button local displayNameButton = Instance.new("TextButton") displayNameButton.Name = "DisplayNameButton" displayNameButton.Size = UDim2.new(0, 40, 0, 24) displayNameButton.Position = UDim2.new(1, -172, 0, 3) displayNameButton.BackgroundColor3 = Color3.fromRGB(150, 100, 50) displayNameButton.BorderSizePixel = 0 displayNameButton.Text = "DN" displayNameButton.TextColor3 = Color3.fromRGB(255, 255, 255) displayNameButton.TextScaled = false displayNameButton.TextSize = 14 displayNameButton.Font = Enum.Font.SourceSansBold displayNameButton.Parent = titleBar styleButton(displayNameButton, Color3.fromRGB(150,100,50), Color3.fromRGB(180,130,60)) -- Create the search box below the title bar local searchBox = Instance.new("TextBox") searchBox.Name = "SearchBox" searchBox.Size = UDim2.new(1, -20, 0, 24) searchBox.Position = UDim2.new(0, 10, 0, 35) searchBox.PlaceholderText = "Search chat..." searchBox.BackgroundColor3 = Color3.fromRGB(50, 50, 50) searchBox.TextColor3 = Color3.fromRGB(255, 255, 255) searchBox.ClearTextOnFocus = false searchBox.Parent = mainFrame -- Create the scrolling frame for chat messages (moved down to fit search) local scrollingFrame = Instance.new("ScrollingFrame") scrollingFrame.Name = "ChatScrollingFrame" scrollingFrame.Size = UDim2.new(1, -10, 1, -80) scrollingFrame.Position = UDim2.new(0, 5, 0, 65) scrollingFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 25) scrollingFrame.BorderSizePixel = 0 scrollingFrame.ScrollBarThickness = 12 scrollingFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 100) scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0) scrollingFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y scrollingFrame.Parent = mainFrame -- Scrolling frame padding and rounded corners local scrollPadding = Instance.new("UIPadding") scrollPadding.PaddingTop = UDim.new(0, 6) scrollPadding.PaddingLeft = UDim.new(0, 6) scrollPadding.PaddingRight = UDim.new(0, 6) scrollPadding.PaddingBottom = UDim.new(0, 6) scrollPadding.Parent = scrollingFrame local scrollCorner = Instance.new("UICorner") scrollCorner.CornerRadius = UDim.new(0, 6) scrollCorner.Parent = scrollingFrame -- Create the layout for chat messages local listLayout = Instance.new("UIListLayout") listLayout.SortOrder = Enum.SortOrder.LayoutOrder listLayout.Padding = UDim.new(0, 2) listLayout.Parent = scrollingFrame -- Create Clear button local clearButton = Instance.new("TextButton") clearButton.Name = "ClearButton" clearButton.Size = UDim2.new(0, 40, 0, 25) clearButton.Position = UDim2.new(1, -215, 0, 2.5) clearButton.BackgroundColor3 = Color3.fromRGB(180, 80, 80) clearButton.BorderSizePixel = 0 clearButton.Text = "CLR" clearButton.TextColor3 = Color3.fromRGB(255, 255, 255) clearButton.TextScaled = true clearButton.Font = Enum.Font.SourceSansBold clearButton.Parent = titleBar -- Create Export button local exportButton = Instance.new("TextButton") exportButton.Name = "ExportButton" exportButton.Size = UDim2.new(0, 40, 0, 25) exportButton.Position = UDim2.new(1, -260, 0, 2.5) exportButton.BackgroundColor3 = Color3.fromRGB(100, 150, 100) exportButton.BorderSizePixel = 0 exportButton.Text = "EXP" exportButton.TextColor3 = Color3.fromRGB(255, 255, 255) exportButton.TextScaled = true exportButton.Font = Enum.Font.SourceSansBold exportButton.Parent = titleBar return screenGui, scrollingFrame, listLayout, closeButton, refreshButton, timestampButton, displayNameButton, clearButton, exportButton, searchBox end -- Initialize the GUI local screenGui, scrollingFrame, listLayout, closeButton, refreshButton, timestampButton, displayNameButton, clearButton, exportButton, searchBox = createChatLoggerGUI() -- Get current timestamp in [HH:MM:SS] format local function getTimestamp() local time = os.date("*t") return string.format("[%02d:%02d:%02d]", time.hour, time.min, time.sec) end -- Add a chat message to the log local function addChatMessage(scrollingFrame, listLayout, playerName, message, skipHistory) -- Store original message data (unless skipping for refresh) if not skipHistory then table.insert(messageHistory, { playerName = playerName, message = message, timestamp = os.time() }) -- enforce history limit if #messageHistory > maxHistory then table.remove(messageHistory, 1) end end -- Get the player object to check display name local playerObj = Players:FindFirstChild(playerName) local displayName = playerObj and playerObj.DisplayName or playerName -- Create the message text with optional timestamp and display name local messageText if showTimestamps then if showDisplayName then messageText = getTimestamp() .. " (" .. displayName .. "): " .. message else messageText = getTimestamp() .. " (" .. playerName .. "): " .. message end else if showDisplayName then messageText = "(" .. displayName .. "): " .. message else messageText = "(" .. playerName .. "): " .. message end end -- Create a label for the chat message local messageLabel = Instance.new("TextLabel") messageLabel.Name = "ChatMessage" messageLabel.Size = UDim2.new(1, -10, 0, 20) messageLabel.BackgroundTransparency = 1 messageLabel.Text = messageText messageLabel.TextColor3 = Color3.fromRGB(255, 255, 255) messageLabel.TextScaled = false messageLabel.TextSize = 14 messageLabel.Font = Enum.Font.SourceSans messageLabel.TextXAlignment = Enum.TextXAlignment.Left messageLabel.TextYAlignment = Enum.TextYAlignment.Top messageLabel.TextWrapped = true messageLabel.Parent = scrollingFrame -- Calculate text size and adjust label height local textSize = TextService:GetTextSize(messageLabel.Text, messageLabel.TextSize, messageLabel.Font, Vector2.new(scrollingFrame.AbsoluteSize.X - 20, math.huge)) messageLabel.Size = UDim2.new(1, -10, 0, textSize.Y) -- Auto-scroll to bottom with multiple methods for reliability game:GetService("RunService").Heartbeat:Wait() task.wait() -- Method 1: Use CanvasPosition if scrollingFrame and scrollingFrame.CanvasSize then scrollingFrame.CanvasPosition = Vector2.new(0, scrollingFrame.CanvasSize.Y.Offset) end -- Method 2: Use CanvasSize offset for more reliable scrolling task.wait() if scrollingFrame then local maxY = 0 for _, child in scrollingFrame:GetChildren() do if child:IsA("TextLabel") then maxY = maxY + child.AbsoluteSize.Y + 2 -- 2 for padding end end scrollingFrame.CanvasPosition = Vector2.new(0, maxY) end end -- Clear all chat messages local function clearChatMessages(scrollingFrame) for _, child in scrollingFrame:GetChildren() do if child:IsA("TextLabel") then child:Destroy() end end end -- Refresh all existing messages with new timestamp and display name format local function refreshMessages(scrollingFrame, listLayout) clearChatMessages(scrollingFrame) local filter = currentSearch:lower() for _, msgData in ipairs(messageHistory) do local playerObj = Players:FindFirstChild(msgData.playerName) local displayName = playerObj and playerObj.DisplayName or msgData.playerName local combined = (msgData.playerName .. " " .. displayName .. " " .. msgData.message):lower() if filter == "" or combined:find(filter, 1, true) then addChatMessage(scrollingFrame, listLayout, msgData.playerName, msgData.message, true) end end end -- Button event handlers closeButton.MouseButton1Click:Connect(function() screenGui:Destroy() end) refreshButton.MouseButton1Click:Connect(function() -- Re-render history with current settings (acts as a refresh) refreshMessages(scrollingFrame, listLayout) end) -- Clear button: clears UI and history if clearButton then clearButton.MouseButton1Click:Connect(function() clearChatMessages(scrollingFrame) messageHistory = {} addChatMessage(scrollingFrame, listLayout, "System", "Chat cleared - logging new messages...") end) end -- Export button: open modal with full log and attempt to copy if exportButton then exportButton.MouseButton1Click:Connect(function() -- Build export text local lines = {} for _, msgData in ipairs(messageHistory) do local timeStr = os.date("%Y-%m-%d %H:%M:%S", msgData.timestamp) table.insert(lines, string.format("[%s] %s: %s", timeStr, msgData.playerName, msgData.message)) end local exportText = table.concat(lines, "\n") -- Try to set clipboard (may fail in normal Roblox environment) local ok, err = pcall(function() if setclipboard then setclipboard(exportText) elseif setclipboard == nil and typeof and type(setclipboard) == "function" then setclipboard(exportText) else error("no clipboard available") end end) if ok then addChatMessage(scrollingFrame, listLayout, "System", "Export copied to clipboard (if supported).") else -- Fallback: show export in a modal TextBox for manual copy local modal = Instance.new("Frame") modal.Name = "ExportModal" modal.Size = UDim2.new(0, 500, 0, 300) modal.Position = UDim2.new(0.5, -250, 0.5, -150) modal.BackgroundColor3 = Color3.fromRGB(40, 40, 40) modal.BorderSizePixel = 0 modal.Parent = screenGui local box = Instance.new("TextBox") box.Name = "ExportBox" box.Size = UDim2.new(1, -20, 1, -60) box.Position = UDim2.new(0, 10, 0, 10) box.Text = exportText box.TextWrapped = true box.TextXAlignment = Enum.TextXAlignment.Left box.TextYAlignment = Enum.TextYAlignment.Top box.MultiLine = true box.ClearTextOnFocus = false box.TextEditable = true box.Parent = modal local closeB = Instance.new("TextButton") closeB.Name = "CloseExport" closeB.Size = UDim2.new(0, 80, 0, 30) closeB.Position = UDim2.new(1, -90, 1, -40) closeB.Text = "Close" closeB.Parent = modal closeB.MouseButton1Click:Connect(function() modal:Destroy() end) addChatMessage(scrollingFrame, listLayout, "System", "Export modal opened — select and copy manually.") end end) end timestampButton.MouseButton1Click:Connect(function() showTimestamps = not showTimestamps if showTimestamps then timestampButton.BackgroundColor3 = Color3.fromRGB(50, 200, 50) refreshMessages(scrollingFrame, listLayout) else timestampButton.BackgroundColor3 = Color3.fromRGB(100, 100, 100) refreshMessages(scrollingFrame, listLayout) end end) displayNameButton.MouseButton1Click:Connect(function() showDisplayName = not showDisplayName if showDisplayName then displayNameButton.BackgroundColor3 = Color3.fromRGB(200, 150, 50) refreshMessages(scrollingFrame, listLayout) else displayNameButton.BackgroundColor3 = Color3.fromRGB(150, 100, 50) refreshMessages(scrollingFrame, listLayout) end end) -- Add a startup message addChatMessage(scrollingFrame, listLayout, "System", "ChatLogger initialized - logging chat messages...") -- Connect to chat messages for all players local function connectPlayerChat(playerToConnect) playerToConnect.Chatted:Connect(function(message) addChatMessage(scrollingFrame, listLayout, playerToConnect.Name, message) end) end -- Connect to local player's chat connectPlayerChat(player) -- Connect to existing players for _, otherPlayer in Players:GetPlayers() do if otherPlayer ~= player then connectPlayerChat(otherPlayer) end end -- Connect to players who join later Players.PlayerAdded:Connect(function(newPlayer) connectPlayerChat(newPlayer) end) -- Search box handler if searchBox then searchBox:GetPropertyChangedSignal("Text"):Connect(function() currentSearch = searchBox.Text or "" refreshMessages(scrollingFrame, listLayout) end) end