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 ]
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
- Yes