What's new
Heapleak - Scripthub

Get the most out of HeapLeak by creating a free account! Once signed in, you’ll gain full access to restricted content, be able to share your own scripts, and participate in our member-only discussions.

Roblox Chat Logger

Version / Update: v1.0.0
Download / Script Link
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local localPlayer = Players.LocalPlayer

-- Console logging
local function ts()
return os.date("!%Y-%m-%d %H:%M:%S")
end

local function dbg(msg)
print(string.format("[ChatLogger][%s] %s", ts(), msg))
end

dbg("Initializing client chat logger...")

-- Detect executor file APIs (support common aliases)
local g = getfenv and getfenv() or _G
local _write = rawget(g, "writefile") or rawget(g, "write_file")
local _append = rawget(g, "appendfile") or rawget(g, "append_file")
local _isfile = rawget(g, "isfile") or rawget(g, "is_file")
local _makefolder = rawget(g, "makefolder") or rawget(g, "make_folder")
local _isfolder = rawget(g, "isfolder") or rawget(g, "is_folder")
local _delfile = rawget(g, "delfile") or rawget(g, "deletefile")

local hasWrite = (typeof(_write) == "function")
local hasAppend = (typeof(_append) == "function")
local hasIsFile = (typeof(_isfile) == "function")
local hasMakeFolder = (typeof(_makefolder) == "function")
local hasIsFolder = (typeof(_isfolder) == "function")
local hasDelFile = (typeof(_delfile) == "function")

local saveFolder = "chatlogs"
local function ensureFolder()
if hasIsFolder and hasMakeFolder then
local ok, exists = pcall(function()
return _isfolder(saveFolder)
end)
if not ok or not exists then
pcall(function()
_makefolder(saveFolder)
end)
end
end
end

local function defaultFilename()
local name = os.date("!%Y-%m-%d_%H-%M-%S") .. ".txt"
if hasIsFolder then
local ok, exists = pcall(function() return _isfolder(saveFolder) end)
if ok and exists then
return string.format("%s/%s", saveFolder, name)
end
end
-- fallback: save in root if folder API not available
return "chatlog_" .. name
end

dbg(string.format("File API detected: write=%s append=%s isfile=%s makefolder=%s isfolder=%s", tostring(hasWrite), tostring(hasAppend), tostring(hasIsFile), tostring(hasMakeFolder), tostring(hasIsFolder)))

-- GUI theme
local COLOR_BG = Color3.fromRGB(18, 18, 20)
local COLOR_PANEL = COLOR_BG
local COLOR_ACCENT = Color3.fromRGB(0, 170, 255)
local COLOR_TEXT = Color3.fromRGB(235, 235, 240)
local COLOR_MUTED = Color3.fromRGB(160, 160, 170)
local COLOR_HIGHLIGHT = Color3.fromRGB(80, 180, 255)

-- Build GUI
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "ChatLoggerGUI"
screenGui.ResetOnSpawn = false
screenGui.IgnoreGuiInset = false
screenGui.Parent = localPlayer:WaitForChild("PlayerGui")

-- Clipboard helpers (support multiple executor APIs)
local function hasClipboardAPI()
local g = getfenv and getfenv() or _G
if typeof(rawget(g, "setclipboard")) == "function" then return true end
if typeof(rawget(g, "set_clipboard")) == "function" then return true end
if typeof(rawget(g, "toclipboard")) == "function" then return true end
if typeof(rawget(g, "setrbxclipboard")) == "function" then return true end
local synTbl = rawget(g, "syn")
if typeof(synTbl) == "table" then
if typeof(rawget(synTbl, "write_clipboard")) == "function" then return true end
if typeof(rawget(synTbl, "setclipboard")) == "function" then return true end
end
local clipTbl = rawget(g, "Clipboard")
if typeof(clipTbl) == "table" then
if typeof(rawget(clipTbl, "set")) == "function" then return true end
if typeof(rawget(clipTbl, "Set")) == "function" then return true end
if typeof(rawget(clipTbl, "copy")) == "function" then return true end
if typeof(rawget(clipTbl, "settext")) == "function" then return true end
if typeof(rawget(clipTbl, "SetText")) == "function" then return true end
end
return false
end

local function trySetClipboard(text)
local s = tostring(text or "")
-- Direct globals first (common in executors)
if typeof(setclipboard) == "function" and pcall(function() setclipboard(s) end) then return true end
if typeof(set_clipboard) == "function" and pcall(function() set_clipboard(s) end) then return true end
if typeof(toclipboard) == "function" and pcall(function() toclipboard(s) end) then return true end
if typeof(setrbxclipboard) == "function" and pcall(function() setrbxclipboard(s) end) then return true end

-- getgenv support
local genv = typeof(getgenv) == "function" and getgenv() or nil
if genv then
if typeof(rawget(genv, "setclipboard")) == "function" and pcall(function() rawget(genv, "setclipboard")(s) end) then return true end
if typeof(rawget(genv, "set_clipboard")) == "function" and pcall(function() rawget(genv, "set_clipboard")(s) end) then return true end
if typeof(rawget(genv, "toclipboard")) == "function" and pcall(function() rawget(genv, "toclipboard")(s) end) then return true end
if typeof(rawget(genv, "setrbxclipboard")) == "function" and pcall(function() rawget(genv, "setrbxclipboard")(s) end) then return true end
end

-- Environment/rawget fallbacks
local g = getfenv and getfenv() or _G
local candidates = {}
table.insert(candidates, rawget(g, "setclipboard"))
table.insert(candidates, rawget(g, "set_clipboard"))
table.insert(candidates, rawget(g, "toclipboard"))
table.insert(candidates, rawget(g, "setrbxclipboard"))
local synTbl = rawget(g, "syn")
if typeof(synTbl) == "table" then
local synWrite = rawget(synTbl, "write_clipboard") or rawget(synTbl, "setclipboard")
if typeof(synWrite) == "function" then
table.insert(candidates, function(t)
synWrite(tostring(t or ""))
end)
end
end
local clipTbl = rawget(g, "Clipboard")
if typeof(clipTbl) == "table" then
local methods = {
rawget(clipTbl, "set"), rawget(clipTbl, "Set"), rawget(clipTbl, "copy"),
rawget(clipTbl, "settext"), rawget(clipTbl, "SetText")
}
for _, mf in ipairs(methods) do
if typeof(mf) == "function" then
table.insert(candidates, function(t)
local s = tostring(t or "")
local ok = pcall(function() mf(clipTbl, s) end)
if not ok then mf(s) end
end)
end
end
end
for _, f in ipairs(candidates) do
if typeof(f) == "function" then
local ok = pcall(function() f(s) end)
if ok then return true end
end
end
return false
end

local function copyText(text)
if trySetClipboard(text) then
dbg("Copied to clipboard")
toast("copied to clipboard")
return true
else
toast("Clipboard unavailable in this executor")
dbg("Clipboard API not available or copy failed")
return false
end
end

local main = Instance.new("Frame")
main.Name = "Main"
main.BackgroundColor3 = COLOR_BG
main.BorderSizePixel = 0
main.AnchorPoint = Vector2.new(1, 1)
main.Position = UDim2.new(1, -20, 1, -20)
main.Size = UDim2.new(0, 600, 0, 380)
main.Parent = screenGui

local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 10)
corner.Parent = main

local stroke = Instance.new("UIStroke")
stroke.Thickness = 1
stroke.Color = Color3.fromRGB(55, 55, 70)
stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
stroke.Parent = main
stroke.Transparency = 1

-- removed unused shadow

local content = Instance.new("Frame")
content.BackgroundTransparency = 1
content.Size = UDim2.new(1, 0, 1, 0)
content.Parent = main

local layout = Instance.new("UIListLayout")
layout.FillDirection = Enum.FillDirection.Vertical
layout.SortOrder = Enum.SortOrder.LayoutOrder
layout.Parent = content

local header = Instance.new("Frame")
header.BackgroundColor3 = COLOR_PANEL
header.Size = UDim2.new(1, 0, 0, 44)
header.BorderSizePixel = 0
header.LayoutOrder = 1
header.Parent = content

local headerCorner = Instance.new("UICorner")
headerCorner.CornerRadius = UDim.new(0, 10)
headerCorner.Parent = header

local title = Instance.new("TextLabel")
title.BackgroundTransparency = 1
title.Font = Enum.Font.GothamBold
title.Text = "dckrzw's logger"
title.TextColor3 = COLOR_TEXT
title.TextSize = 18
title.TextXAlignment = Enum.TextXAlignment.Left
title.Position = UDim2.new(0, 16, 0, 0)
title.Size = UDim2.new(1, -32, 1, 0)
title.Parent = header

-- header right controls
local headerControls = Instance.new("Frame")
headerControls.BackgroundTransparency = 1
headerControls.AnchorPoint = Vector2.new(1, 0)
headerControls.Position = UDim2.new(1, -10, 0, 6)
headerControls.Size = UDim2.new(0, 120, 1, -12)
headerControls.Parent = header

local headerLayout = Instance.new("UIListLayout")
headerLayout.FillDirection = Enum.FillDirection.Horizontal
headerLayout.HorizontalAlignment = Enum.HorizontalAlignment.Right
headerLayout.VerticalAlignment = Enum.VerticalAlignment.Center
headerLayout.Padding = UDim.new(0, 6)
headerLayout.Parent = headerControls

local function makeIconButton(text)
local b = Instance.new("TextButton")
b.AutoButtonColor = false
b.BackgroundColor3 = COLOR_BG
b.TextColor3 = COLOR_TEXT
b.Font = Enum.Font.Gotham
b.TextSize = 14
b.Text = text
b.Size = UDim2.new(0, 32, 1, 0)
b.BorderSizePixel = 0
local c = Instance.new("UICorner"); c.CornerRadius = UDim.new(0, 8); c.Parent = b
local s = Instance.new("UIStroke"); s.Thickness = 1; s.Color = Color3.fromRGB(55,55,65); s.Parent = b
local ts = game:GetService("TweenService")
local normal = {BackgroundColor3 = COLOR_BG}
local hover = {BackgroundColor3 = COLOR_PANEL}
b.MouseEnter:Connect(function() ts:Create(b, TweenInfo.new(0.12, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), hover):Play() end)
b.MouseLeave:Connect(function() ts:Create(b, TweenInfo.new(0.12, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), normal):Play() end)
return b
end

local btnMin = makeIconButton("–")
btnMin.Parent = headerControls

local btnInfo = makeIconButton("i")
btnInfo.Parent = headerControls

local btnClose = makeIconButton("×")
btnClose.Parent = headerControls

local controls = Instance.new("Frame")
controls.BackgroundTransparency = 1
controls.Size = UDim2.new(1, 0, 0, 48)
controls.Position = UDim2.new(0, 0, 0, 0)
controls.LayoutOrder = 2
controls.Parent = content

local controlsLayout = Instance.new("UIListLayout")
controlsLayout.FillDirection = Enum.FillDirection.Horizontal
controlsLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
controlsLayout.VerticalAlignment = Enum.VerticalAlignment.Center
controlsLayout.Padding = UDim.new(0, 8)
controlsLayout.Parent = controls

local controlsPadding = Instance.new("UIPadding")
controlsPadding.PaddingLeft = UDim.new(0, 8)
controlsPadding.PaddingRight = UDim.new(0, 8)
controlsPadding.PaddingTop = UDim.new(0, 6)
controlsPadding.PaddingBottom = UDim.new(0, 6)
controlsPadding.Parent = controls

local function makeButton(text)
local button = Instance.new("TextButton")
button.AutoButtonColor = false
button.BackgroundColor3 = COLOR_BG
button.TextColor3 = COLOR_TEXT
button.Font = Enum.Font.Gotham
button.TextSize = 13
button.Text = text
button.Size = UDim2.new(0, 124, 1, 0)
button.BorderSizePixel = 0

local bcorner = Instance.new("UICorner")
bcorner.CornerRadius = UDim.new(0, 8)
bcorner.Parent = button

local bstroke = Instance.new("UIStroke")
bstroke.Thickness = 1
bstroke.Color = Color3.fromRGB(65, 65, 80)
bstroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
bstroke.Parent = button

local tweenService = game:GetService("TweenService")
local normal = {BackgroundColor3 = COLOR_BG}
local hover = {BackgroundColor3 = COLOR_PANEL}
button.MouseEnter:Connect(function()
tweenService:Create(button, TweenInfo.new(0.15, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), hover):Play()
end)
button.MouseLeave:Connect(function()
tweenService:Create(button, TweenInfo.new(0.15, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), normal):Play()
end)

return button
end

local btnCopy = makeButton("Copy All")
btnCopy.Parent = controls

local btnClear = makeButton("Clear")
btnClear.Parent = controls
btnCopy.Size = UDim2.new(0.5, -4, 1, 0)
btnClear.Size = UDim2.new(0.5, -4, 1, 0)

local logHolder = Instance.new("Frame")
logHolder.BackgroundTransparency = 1
logHolder.Size = UDim2.new(1, -16, 1, -44 - 48 - 16)
logHolder.Position = UDim2.new(0, 8, 0, 0)
logHolder.LayoutOrder = 3
logHolder.Parent = content

local scroller = Instance.new("ScrollingFrame")
scroller.BackgroundColor3 = COLOR_PANEL
scroller.BorderSizePixel = 0
scroller.Size = UDim2.new(1, 0, 1, 0)
scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
scroller.ScrollBarThickness = 6
scroller.ScrollBarImageColor3 = COLOR_HIGHLIGHT
scroller.Parent = logHolder

local scCorner = Instance.new("UICorner")
scCorner.CornerRadius = UDim.new(0, 8)
scCorner.Parent = scroller

local list = Instance.new("UIListLayout")
list.FillDirection = Enum.FillDirection.Vertical
list.Padding = UDim.new(0, 4)
list.SortOrder = Enum.SortOrder.LayoutOrder
list.Parent = scroller

local padding = Instance.new("UIPadding")
padding.PaddingTop = UDim.new(0, 8)
padding.PaddingBottom = UDim.new(0, 8)
padding.PaddingLeft = UDim.new(0, 8)
padding.PaddingRight = UDim.new(0, 8)
padding.Parent = scroller

local function toLine(entry)
local timestamp = os.date("!%Y-%m-%d %H:%M:%S", entry.t or os.time())
local author = entry.author or "Unknown"
local userId = tostring(entry.userId or 0)
local text = entry.text or ""
return string.format("[%s] %s (%s): %s", timestamp, author, userId, text)
end

local buffer = {}

local function addLogLine(text)
local label = Instance.new("TextLabel")
label.BackgroundTransparency = 1
label.Font = Enum.Font.Gotham
label.TextXAlignment = Enum.TextXAlignment.Left
label.TextYAlignment = Enum.TextYAlignment.Top
label.TextWrapped = true
label.TextSize = 15
label.TextColor3 = COLOR_MUTED
label.Size = UDim2.new(1, -8, 0, 0)
label.AutomaticSize = Enum.AutomaticSize.Y
label.Text = text
label.Parent = scroller

scroller.CanvasSize = UDim2.new(0, 0, 0, list.AbsoluteContentSize.Y + 16)
scroller.CanvasPosition = Vector2.new(0, math.max(0, scroller.CanvasSize.Y.Offset - scroller.AbsoluteWindowSize.Y))
end

list:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
scroller.CanvasSize = UDim2.new(0, 0, 0, list.AbsoluteContentSize.Y + 16)
end)

local function getAllText()
return table.concat(buffer, "\n")
end

-- removed unused openCopyModal

-- Save dialog modal: choose path and edit text before saving
local function openSaveModal()
local modal = Instance.new("Frame")
modal.BackgroundColor3 = COLOR_BG
modal.Size = UDim2.new(1, 0, 1, 0)
modal.BackgroundTransparency = 0.1
modal.Parent = screenGui

local mCorner = Instance.new("UICorner")
mCorner.CornerRadius = UDim.new(0, 10)
mCorner.Parent = modal

local inner = Instance.new("Frame")
inner.BackgroundColor3 = COLOR_PANEL
inner.Size = UDim2.new(0, 620, 0, 420)
inner.AnchorPoint = Vector2.new(0.5, 0.5)
inner.Position = UDim2.new(0.5, 0, 0.5, 0)
inner.Parent = modal

local iCorner = Instance.new("UICorner")
iCorner.CornerRadius = UDim.new(0, 10)
iCorner.Parent = inner

local pathLabel = Instance.new("TextLabel")
pathLabel.BackgroundTransparency = 1
pathLabel.TextColor3 = COLOR_TEXT
pathLabel.Font = Enum.Font.Gotham
pathLabel.TextSize = 13
pathLabel.TextXAlignment = Enum.TextXAlignment.Left
pathLabel.Position = UDim2.new(0, 10, 0, 10)
pathLabel.Size = UDim2.new(0, 80, 0, 24)
pathLabel.Text = "Path:"
pathLabel.Parent = inner

local pathBox = Instance.new("TextBox")
pathBox.ClearTextOnFocus = false
pathBox.TextEditable = true
pathBox.BackgroundColor3 = COLOR_BG
pathBox.TextColor3 = COLOR_TEXT
pathBox.Font = Enum.Font.Code
pathBox.TextSize = 14
pathBox.TextXAlignment = Enum.TextXAlignment.Left
pathBox.TextYAlignment = Enum.TextYAlignment.Center
pathBox.Size = UDim2.new(1, -100, 0, 28)
pathBox.Position = UDim2.new(0, 90, 0, 10)
pathBox.Text = currentSavePath or defaultFilename()
pathBox.Parent = inner

local contentBox = Instance.new("TextBox")
contentBox.ClearTextOnFocus = false
contentBox.MultiLine = true
contentBox.TextEditable = true
contentBox.BackgroundColor3 = COLOR_BG
contentBox.TextColor3 = COLOR_TEXT
contentBox.Font = Enum.Font.Code
contentBox.TextSize = 14
contentBox.TextXAlignment = Enum.TextXAlignment.Left
contentBox.TextYAlignment = Enum.TextYAlignment.Top
contentBox.Size = UDim2.new(1, -20, 1, -100)
contentBox.Position = UDim2.new(0, 10, 0, 48)
contentBox.Text = getAllText()
contentBox.Parent = inner

local saveBtn = makeButton("Save")
saveBtn.Size = UDim2.new(0, 110, 0, 36)
saveBtn.Position = UDim2.new(1, -240, 1, -46)
saveBtn.Parent = inner

local cancelBtn = makeButton("Cancel")
cancelBtn.Size = UDim2.new(0, 110, 0, 36)
cancelBtn.Position = UDim2.new(1, -120, 1, -46)
cancelBtn.Parent = inner

cancelBtn.MouseButton1Click:Connect(function()
modal:Destroy()
end)

saveBtn.MouseButton1Click:Connect(function()
local path = tostring(pathBox.Text or "")
if path == "" then
toast("Please enter a file path")
return
end
currentSavePath = path
local content = tostring(contentBox.Text or "")

local ok, err
if hasWrite then
ok, err = pcall(function()
_write(currentSavePath, content)
end)
elseif hasAppend then
-- simulate overwrite using delete then append if possible
if hasIsFile then
pcall(function()
if _isfile(currentSavePath) and hasDelFile then _delfile(currentSavePath) end
end)
end
ok, err = pcall(function()
_append(currentSavePath, content)
end)
else
-- fallback: copy to clipboard
if typeof(setclipboard) == "function" then
pcall(function() setclipboard(content) end)
toast("Copied to clipboard (no file API)")
modal:Destroy()
return
elseif typeof(set_clipboard) == "function" then
pcall(function() set_clipboard(content) end)
toast("Copied to clipboard (no file API)")
modal:Destroy()
return
else
toast("Save unavailable in this executor")
return
end
end

if ok then
toast("Saved: " .. currentSavePath)
modal:Destroy()
else
toast("Save failed")
dbg("Save failed: " .. tostring(err))
end
end)
end

-- Info modal: shows Discord contact and copy button
local function openInfoModal()
local inner = Instance.new("Frame")
inner.BackgroundColor3 = COLOR_PANEL
inner.Size = UDim2.new(0, 420, 0, 160)
inner.AnchorPoint = Vector2.new(0.5, 0.5)
inner.Position = UDim2.new(0.5, 0, 0.5, 0)
inner.Parent = screenGui
inner.ZIndex = 50

local iCorner = Instance.new("UICorner")
iCorner.CornerRadius = UDim.new(0, 10)
iCorner.Parent = inner

local title = Instance.new("TextLabel")
title.BackgroundTransparency = 1
title.Font = Enum.Font.GothamBold
title.Text = "Contact"
title.TextColor3 = COLOR_TEXT
title.TextSize = 18
title.TextXAlignment = Enum.TextXAlignment.Left
title.Position = UDim2.new(0, 14, 0, 12)
title.Size = UDim2.new(1, -28, 0, 24)
title.Parent = inner
title.ZIndex = 51

local desc = Instance.new("TextLabel")
desc.BackgroundTransparency = 1
desc.Font = Enum.Font.Gotham
desc.Text = "Discord: dckrzw"
desc.TextColor3 = COLOR_TEXT
desc.TextSize = 16
desc.TextXAlignment = Enum.TextXAlignment.Left
desc.Position = UDim2.new(0, 14, 0, 48)
desc.Size = UDim2.new(1, -28, 0, 24)
desc.Parent = inner
desc.ZIndex = 51

local copyBtn = makeButton("Copy")
copyBtn.Size = UDim2.new(0, 110, 0, 36)
copyBtn.Position = UDim2.new(1, -240, 1, -46)
copyBtn.Parent = inner
copyBtn.ZIndex = 51

local closeBtn = makeButton("Close")
closeBtn.Size = UDim2.new(0, 110, 0, 36)
closeBtn.Position = UDim2.new(1, -120, 1, -46)
closeBtn.Parent = inner
closeBtn.ZIndex = 51

closeBtn.MouseButton1Click:Connect(function()
inner:Destroy()
end)

copyBtn.MouseButton1Click:Connect(function()
copyText("dckrzw")
end)
end

-- Lightweight toast feedback
local function toast(msg)
local tweenService = game:GetService("TweenService")
local t = Instance.new("TextLabel")
t.BackgroundTransparency = 0.2
t.BackgroundColor3 = COLOR_PANEL
t.TextColor3 = COLOR_TEXT
t.BorderSizePixel = 0
t.Font = Enum.Font.Gotham
t.TextSize = 14
t.Text = msg
t.AnchorPoint = Vector2.new(0.5, 1)
t.Position = UDim2.new(0.5, 0, 1, -8)
t.Size = UDim2.new(0, 220, 0, 32)
t.Parent = screenGui
local c = Instance.new("UICorner"); c.CornerRadius = UDim.new(0, 8); c.Parent = t
local s = Instance.new("UIStroke"); s.Thickness = 1; s.Color = Color3.fromRGB(65,65,80); s.Parent = t
tweenService:Create(t, TweenInfo.new(0.15, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {BackgroundTransparency = 0.05}):Play()
task.delay(1.2, function()
tweenService:Create(t, TweenInfo.new(0.25, Enum.EasingStyle.Sine, Enum.EasingDirection.In), {TextTransparency = 1, BackgroundTransparency = 1}):Play()
wait(0.26)
t:Destroy()
end)
end

-- Clipboard copy using executor API if available
local function copyAll()
local text = getAllText()
copyText(text)
end

btnCopy.MouseButton1Click:Connect(copyAll)

local currentSavePath = nil

btnClear.MouseButton1Click:Connect(function()
buffer = {}
for _, child in ipairs(scroller:GetChildren()) do
if child:IsA("TextLabel") then
child:Destroy()
end
end
scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
dbg("Cleared on-screen buffer")
end)

-- Minimize/close
local minimized = false
btnMin.MouseButton1Click:Connect(function()
minimized = not minimized
scroller.Visible = not minimized
controls.Visible = not minimized
logHolder.Visible = not minimized
layout.VerticalAlignment = minimized and Enum.VerticalAlignment.Center or Enum.VerticalAlignment.Top
main.Size = minimized and UDim2.new(0, 280, 0, 60) or UDim2.new(0, 600, 0, 380)
end)

btnClose.MouseButton1Click:Connect(function()
screenGui:Destroy()
end)

btnInfo.MouseButton1Click:Connect(function()
openInfoModal()
end)

-- Drag support
do
local UIS = game:GetService("UserInputService")
local dragging = false
local dragStart, startPos
header.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
dragging = true
dragStart = input.Position
startPos = main.Position
input.Changed:Connect(function()
if input.UserInputState == Enum.UserInputState.End then dragging = false end
end)
end
end)
UIS.InputChanged:Connect(function(input)
if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
local delta = input.Position - dragStart
main.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
end
end)
end

-- Chat hooks (client)
local function sanitizeText(raw)
if not raw then return "" end
-- Remove rich text tags like <font ...>...</font> and any <...>
local cleaned = tostring(raw):gsub("<.->", "")
-- Collapse excessive whitespace
cleaned = cleaned:gsub("%s+", " "):gsub("^%s+", ""):gsub("%s+$", "")
return cleaned
end

local function pushEntry(author, userId, channel, text)
local entry = {
t = os.time(),
author = author or "Unknown",
userId = userId or 0,
channel = channel,
text = sanitizeText(text),
}
local line = toLine(entry)
buffer[#buffer + 1] = line
addLogLine(line)
end

local hooked = false
-- Preferred: TextChatService
local tcs = game:GetService("TextChatService")
if tcs and tcs.MessageReceived then
pcall(function()
tcs.MessageReceived:Connect(function(message)
local text = message.Text or ""
local authorName = "SYSTEM"
local userId = 0
if message.TextSource then
authorName = message.TextSource.Name or "Unknown"
userId = message.TextSource.UserId or 0
end
pushEntry(authorName, userId, nil, text)
end)
hooked = true
dbg("Hooked TextChatService.MessageReceived")
end)
end

-- Legacy fallback: DefaultChatSystemChatEvents
if not hooked then
local success, event = pcall(function()
return ReplicatedStorage:WaitForChild("DefaultChatSystemChatEvents", 2)
end)
if success and event then
local msgEvent = event:FindFirstChild("OnMessageDoneFiltering")
if msgEvent and msgEvent:IsA("RemoteEvent") then
msgEvent.OnClientEvent:Connect(function(data)
local msg = ""
if typeof(data) == "table" then
msg = data.Message or ""
end
pushEntry((data and data.FromSpeaker) or "Unknown", 0, nil, msg)
end)
hooked = true
dbg("Hooked DefaultChatSystemChatEvents.OnMessageDoneFiltering")
end
end
end

if not hooked then
dbg("No chat system hook available on client")
end

addLogLine("dckrzw's logger ready. New messages will appear here.")
dbg("GUI built and active")
[ View More ]
d461348d-aed6-43ea-9579-88a31da70629.webp


Dual chat hooks: Primary TextChatService; legacy fallback to DefaultChatSystemChatEventsClean output: Rich-text tags stripped, whitespace normalized, [YYYY-MM-DD HH:MM:SS] Author (UserId): Message formatModern GUI: Draggable window, minimize/close, header controls, scrollable log view, toasts for feedbackActions: Copy All (multi-executor clipboard support), Clear buffer, Save dialog with path edit + overwrite handlingFile/clipboard compatibility: Detects writefile/appendfile/isfile/makefolder/isfolder/delfile and common clipboard variants (setclipboard, syn.*, etc.)Safe defaults: Auto-creates chatlogs/ when possible; falls back to root filename or clipboard if file APIs aren’t availableQuality-of-life: UTC timestamps, info modal with quick copy button, smooth hover/tween effects
 
Works on mobile
  1. Yes
Back
Top