23 8 No KeyWrite a Letter Script (Delta Mobile) – AutoFishing: Auto Cast, Auto Minigame
Write a Letter · RobloxDescription
AutoFishing is a Write a Letter script you can run in Delta on Android, iOS or PC. It includes auto cast, auto minigame, auto recast and more. It is open source and the panel has a START/STOP button with live status. Created by PerplexWare. No key.
Key Features
- Auto Cast
- Auto Minigame
- Auto Recast after each catch
- Strength setting (tap to change)
- Hook delay, cast delay and click rate settings
- Invert keys option
- Hotkeys: F toggle, G strength, RCtrl hide
Most people play Write a Letter to write notes and collect other players’ letters in a journal, but there’s fishing too. AutoFishing, fixed by PerplexWare from an original by @Ziphyx on ScriptBlox, is a Write a Letter script that takes care of the whole fishing loop: cast, wait, hook, play the minigame, then go again. The cover screenshot was taken in the game, so the panel you’ll see looks like that.
What happens on each cast
Press START with a rod in your inventory (it looks for a tool with “fishing”, “rod” or “caña” in the name). The status line takes you through the cycle: Casting…, Waiting for fish…, Bite! Hooking…, Minigame Active, and then either “Caught: [fish]!” or “Fish escaped…”. In the minigame, it reads where the target sits inside the ring, holds W, A, S or D to bring it back to the middle, and clicks whenever the target is inside the inner circle. After each catch it resets the rod and casts again.
Settings worth knowing
- Strength has three levels you cycle through by tapping: Low, Medium and High, which the code sets at 0.5, 1 and 1.5 seconds after each cast.
- Hook delay (0.1 s by default), cast delay (1.5 s) and click rate (0.10 s) are under Settings [+], with − and + buttons.
- Invert keys starts ON and reverses the WASD steering.
- Keyboard shortcuts: F starts or stops, G changes strength, Right Ctrl hides the panel. On a phone, tap the buttons instead.
Setup
No key, and nothing to download. The code box is the full source. Paste it into Delta. The tested list also covers Codex, Fluxus, Arceus X and PC. Running it again cleans up the previous copy first. The steering sends virtual key presses, so it depends on your executor’s VirtualInputManager support. If that’s missing, the panel still casts and hooks, but the minigame won’t be steered.
Write a Letter FAQ
The target keeps drifting away from the centre. Why? The steering is probably reversed. Switch Invert keys and watch the next minigame.
Do I need to buy a rod? You need one in your backpack or hand. Without one there’s nothing for the script to cast.
Angler tip: leave cast delay at around 1.5 seconds for the first few catches and watch the status line. Once you’re consistently seeing “Caught”, lower it a step at a time so you spend less time waiting between casts.
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local VirtualInputManager = nil
pcall(function()
VirtualInputManager = game:GetService("VirtualInputManager")
end)
local LocalPlayer = Players.LocalPlayer
local env = (getgenv and getgenv()) or _G
if type(env.__AutoFishingCleanup) == "function" then
pcall(env.__AutoFishingCleanup)
env.__AutoFishingCleanup = nil
end
if not LocalPlayer.Character then
LocalPlayer.CharacterAdded:Wait()
end
local Config = {
Enabled = false,
StrengthOrder = { "Low", "Medium", "High" },
StrengthModes = { Low = 0.5, Medium = 1.0, High = 1.5 },
CurrentMode = "High",
CastStrength = 1.0,
AutoHookDelay = 0.1, -- Default Config
AutoCastDelay = 1.5,
MinigameClickRate = 0.10,
InvertKeys = true,
}
Config.CastStrength = Config.StrengthModes[Config.CurrentMode]
local DEADZONE = 0.05
local CAST_TIMEOUT = 90
local connections = {}
local currentState = ""
local loopThread = nil
local eventConn = nil
local keysDown = { W = false, A = false, S = false, D = false }
local KEYCODES = {
W = Enum.KeyCode.W,
A = Enum.KeyCode.A,
S = Enum.KeyCode.S,
D = Enum.KeyCode.D,
}
local minigame = { active = false, thread = nil, conn = nil, seed = nil, startedAt = 0, guiSeen = false }
local function track(conn)
connections[#connections + 1] = conn
return conn
end
local updateUI = function() end
local function setState(text)
currentState = text
updateUI()
end
local COLOR = {
bg = Color3.fromRGB(30, 30, 30),
row = Color3.fromRGB(50, 50, 50),
green = Color3.fromRGB(60, 170, 90),
red = Color3.fromRGB(190, 65, 65),
blue = Color3.fromRGB(65, 110, 190),
white = Color3.fromRGB(255, 255, 255),
dim = Color3.fromRGB(160, 160, 160),
on = Color3.fromRGB(100, 255, 100),
off = Color3.fromRGB(255, 100, 100),
state = Color3.fromRGB(255, 200, 100),
}
local function create(class, props, parent)
local inst = Instance.new(class)
for k, v in pairs(props) do
inst[k] = v
end
if parent then
inst.Parent = parent
end
return inst
end
local function corner(parent, radius)
return create("UICorner", { CornerRadius = UDim.new(0, radius or 6) }, parent)
end
local function label(parent, props)
props.BackgroundTransparency = 1
props.TextColor3 = props.TextColor3 or COLOR.white
props.Font = props.Font or Enum.Font.Gotham
props.TextSize = props.TextSize or 13
return create("TextLabel", props, parent)
end
local function button(parent, props)
props.TextColor3 = props.TextColor3 or COLOR.white
props.Font = props.Font or Enum.Font.GothamBold
props.TextSize = props.TextSize or 14
props.BorderSizePixel = 0
props.AutoButtonColor = true
local b = create("TextButton", props, parent)
corner(b, 6)
return b
end
local playerGui = LocalPlayer:WaitForChild("PlayerGui")
for _, old in ipairs(playerGui:GetChildren()) do
if old.Name == "AutoFishing" or old.Name == "AutoFishingUI" then
old:Destroy()
end
end
local gui = create("ScreenGui", {
Name = "AutoFishing",
ResetOnSpawn = false,
DisplayOrder = 100,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
}, nil)
gui.Parent = playerGui
local panel = create("Frame", {
Name = "StatusFrame",
Size = UDim2.new(0, 244, 0, 0),
AutomaticSize = Enum.AutomaticSize.Y,
Position = UDim2.new(0, 10, 0.5, -110),
BackgroundColor3 = COLOR.bg,
BackgroundTransparency = 0.15,
BorderSizePixel = 0,
}, gui)
corner(panel, 8)
create("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 6),
}, panel)
create("UIPadding", {
PaddingTop = UDim.new(0, 6),
PaddingBottom = UDim.new(0, 8),
PaddingLeft = UDim.new(0, 8),
PaddingRight = UDim.new(0, 8),
}, panel)
local titleBar = create("Frame", {
Name = "TitleBar",
Size = UDim2.new(1, 0, 0, 28),
BackgroundTransparency = 1,
LayoutOrder = 1,
}, panel)
local titleLabel = label(titleBar, {
Name = "Title",
Size = UDim2.new(1, -34, 1, 0),
Text = "AutoFishing",
TextSize = 18,
Font = Enum.Font.GothamBold,
TextXAlignment = Enum.TextXAlignment.Left,
Active = true,
})
local minimizeBtn = button(titleBar, {
Name = "Minimize",
Size = UDim2.new(0, 30, 0, 26),
Position = UDim2.new(1, -30, 0, 1),
Text = "-",
BackgroundColor3 = COLOR.row,
})
local statusLabel = label(panel, {
Name = "Status",
Size = UDim2.new(1, 0, 0, 18),
Text = "Status: OFF",
TextColor3 = COLOR.off,
TextSize = 14,
TextXAlignment = Enum.TextXAlignment.Left,
LayoutOrder = 2,
})
local stateLabel = label(panel, {
Name = "CurrentState",
Size = UDim2.new(1, 0, 0, 16),
Text = "",
TextColor3 = COLOR.state,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
LayoutOrder = 3,
})
local toggleBtn = button(panel, {
Name = "ToggleButton",
Size = UDim2.new(1, 0, 0, 40),
Text = "START",
BackgroundColor3 = COLOR.green,
LayoutOrder = 4,
})
local strengthBtn = button(panel, {
Name = "StrengthButton",
Size = UDim2.new(1, 0, 0, 36),
Text = "Strength: ...",
BackgroundColor3 = COLOR.blue,
LayoutOrder = 5,
})
local settingsBtn = button(panel, {
Name = "SettingsButton",
Size = UDim2.new(1, 0, 0, 32),
Text = "Settings [+]",
BackgroundColor3 = COLOR.row,
TextSize = 13,
LayoutOrder = 6,
})
local settingsFrame = create("Frame", {
Name = "SettingsFrame",
Size = UDim2.new(1, 0, 0, 0),
AutomaticSize = Enum.AutomaticSize.Y,
BackgroundTransparency = 1,
Visible = false,
LayoutOrder = 7,
}, panel)
create("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 5),
}, settingsFrame)
local refreshers = {}
local function round2(n)
return math.floor(n * 100 + 0.5) / 100
end
local function stepper(order, text, key, step, minV, maxV)
local row = create("Frame", {
Size = UDim2.new(1, 0, 0, 34),
BackgroundTransparency = 1,
LayoutOrder = order,
}, settingsFrame)
label(row, {
Size = UDim2.new(0.38, 0, 1, 0),
Text = text,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
})
local minus = button(row, {
Size = UDim2.new(0.17, 0, 1, 0),
Position = UDim2.new(0.39, 0, 0, 0),
Text = "-",
BackgroundColor3 = COLOR.row,
TextSize = 18,
})
local value = label(row, {
Size = UDim2.new(0.26, 0, 1, 0),
Position = UDim2.new(0.57, 0, 0, 0),
Text = "",
TextSize = 13,
Font = Enum.Font.GothamBold,
})
local plus = button(row, {
Size = UDim2.new(0.17, 0, 1, 0),
Position = UDim2.new(0.83, 0, 0, 0),
Text = "+",
BackgroundColor3 = COLOR.row,
TextSize = 18,
})
local function refresh()
value.Text = string.format("%.2fs", Config[key])
end
local function change(delta)
local v = round2(Config[key] + delta)
if v < minV then v = minV end
if v > maxV then v = maxV end
Config[key] = v
refresh()
end
track(minus.Activated:Connect(function() change(-step) end))
track(plus.Activated:Connect(function() change(step) end))
refreshers[#refreshers + 1] = refresh
refresh()
end
stepper(1, "Hook delay", "AutoHookDelay", 0.05, 0, 3)
stepper(2, "Cast delay", "AutoCastDelay", 0.25, 0.25, 15)
stepper(3, "Click rate", "MinigameClickRate", 0.01, 0.02, 1)
local invertBtn = button(settingsFrame, {
Name = "InvertButton",
Size = UDim2.new(1, 0, 0, 34),
Text = "Invert keys: OFF",
BackgroundColor3 = COLOR.row,
TextSize = 13,
LayoutOrder = 4,
})
label(panel, {
Name = "Credits",
Size = UDim2.new(1, 0, 0, 28),
Text = "Original by: @Ziphyx (Scriptblox)\nFix by: perplexware.vercel.app\[email protected] (discord)",
TextColor3 = COLOR.dim,
TextSize = 10,
LayoutOrder = 8,
})
if UserInputService.KeyboardEnabled then
label(panel, {
Name = "Controls",
Size = UDim2.new(1, 0, 0, 14),
Text = "F: Toggle | G: Strength | RCtrl: Hide",
TextColor3 = COLOR.dim,
TextSize = 10,
LayoutOrder = 9,
})
end
local mini = create("Frame", {
Name = "MiniBar",
Size = UDim2.new(0, 120, 0, 36),
Position = panel.Position,
BackgroundColor3 = COLOR.bg,
BackgroundTransparency = 0.15,
BorderSizePixel = 0,
Visible = false,
}, gui)
corner(mini, 8)
local miniGrip = label(mini, {
Size = UDim2.new(0, 24, 1, 0),
Text = "::",
Font = Enum.Font.GothamBold,
TextSize = 16,
Active = true,
})
local miniToggle = button(mini, {
Name = "MiniToggle",
Size = UDim2.new(0, 62, 0, 28),
Position = UDim2.new(0, 26, 0, 4),
Text = "OFF",
BackgroundColor3 = COLOR.red,
})
local miniExpand = button(mini, {
Name = "MiniExpand",
Size = UDim2.new(0, 28, 0, 28),
Position = UDim2.new(0, 90, 0, 4),
Text = "+",
BackgroundColor3 = COLOR.row,
})
local function setMinimized(state)
if state then
mini.Position = panel.Position
panel.Visible = false
mini.Visible = true
else
panel.Position = mini.Position
mini.Visible = false
panel.Visible = true
end
end
local function makeDraggable(handle, target)
local dragging = false
local dragStart, startPos
track(handle.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1
or input.UserInputType == Enum.UserInputType.Touch then
dragging = true
dragStart = input.Position
startPos = target.Position
end
end))
track(UserInputService.InputChanged:Connect(function(input)
if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement
or input.UserInputType == Enum.UserInputType.Touch) then
local delta = input.Position - dragStart
target.Position = UDim2.new(
startPos.X.Scale, startPos.X.Offset + delta.X,
startPos.Y.Scale, startPos.Y.Offset + delta.Y
)
end
end))
track(UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1
or input.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end))
end
makeDraggable(titleLabel, panel)
makeDraggable(miniGrip, mini)
updateUI = function()
if Config.Enabled then
statusLabel.Text = "Status: ON"
statusLabel.TextColor3 = COLOR.on
stateLabel.Text = currentState
toggleBtn.Text = "STOP"
toggleBtn.BackgroundColor3 = COLOR.red
miniToggle.Text = "ON"
miniToggle.BackgroundColor3 = COLOR.green
else
statusLabel.Text = "Status: OFF"
statusLabel.TextColor3 = COLOR.off
stateLabel.Text = ""
toggleBtn.Text = "START"
toggleBtn.BackgroundColor3 = COLOR.green
miniToggle.Text = "OFF"
miniToggle.BackgroundColor3 = COLOR.red
end
strengthBtn.Text = "Strength: " .. Config.CurrentMode .. " (tap to change)"
invertBtn.Text = "Invert keys: " .. (Config.InvertKeys and "ON" or "OFF")
invertBtn.BackgroundColor3 = Config.InvertKeys and COLOR.blue or COLOR.row
for _, refresh in ipairs(refreshers) do
refresh()
end
end
local function sendKey(keyCode, down)
if not VirtualInputManager then
return
end
pcall(function()
VirtualInputManager:SendKeyEvent(down, keyCode, false, game)
end)
end
local function setKey(name, want)
if keysDown[name] ~= want then
sendKey(KEYCODES[name], want)
keysDown[name] = want
end
end
local function releaseAllKeys()
for name, isDown in pairs(keysDown) do
if isDown then
sendKey(KEYCODES[name], false)
keysDown[name] = false
end
end
end
local function steerKeys(offsetX, offsetY)
if math.sqrt(offsetX * offsetX + offsetY * offsetY) < DEADZONE then
releaseAllKeys()
return
end
local sign = Config.InvertKeys and -1 or 1
local ox, oy = offsetX * sign, offsetY * sign
setKey("W", oy < -DEADZONE) -- target above centre
setKey("S", oy > DEADZONE) -- target below centre
setKey("A", ox < -DEADZONE) -- target left of centre
setKey("D", ox > DEADZONE) -- target right of centre
end
local function isRod(item)
if not item:IsA("Tool") then
return false
end
local name = item.Name:lower()
return name:find("fishing", 1, true) ~= nil
or name:find("rod", 1, true) ~= nil
or name:find("caña", 1, true) ~= nil
end
local function isAlive()
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
return hum ~= nil and hum.Health > 0
end
local function findRod()
local char = LocalPlayer.Character
if not char then
return nil
end
for _, item in ipairs(char:GetChildren()) do
if isRod(item) then
return item
end
end
local backpack = LocalPlayer:FindFirstChild("Backpack")
if backpack then
for _, item in ipairs(backpack:GetChildren()) do
if isRod(item) then
return item
end
end
end
return nil
end
local function equipRod(rod)
if not rod then
return false
end
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if not hum then
return false
end
if rod.Parent == char then
return true
end
hum:EquipTool(rod)
task.wait(0.5)
return rod.Parent == char
end
local function unequipAll()
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if not hum then
return
end
hum:UnequipTools()
task.wait(0.3)
end
local function resetRod(rod)
if not rod then
return false
end
setState("Resetting rod...")
unequipAll()
task.wait(0.5)
return equipRod(rod)
end
local function activateRod(rod)
if rod and rod:IsA("Tool") then
rod:Activate()
end
end
local function getMinigameState()
local state = {
moveDirection = Vector3.zero,
isInSuccessZone = false,
targetDistance = 1,
offsetX = 0,
offsetY = 0,
}
local pg = LocalPlayer:FindFirstChild("PlayerGui")
local frame = pg and pg:FindFirstChild("FishMinigame")
local outer = frame and frame:FindFirstChild("OuterRing")
if not outer then
return state
end
local target = outer:FindFirstChild("Target")
local inner = outer:FindFirstChild("InnerRing")
if not target or not target:IsA("GuiObject") then
return state
end
local ox = target.Position.X.Scale - 0.5
local oy = target.Position.Y.Scale - 0.5
local dist = math.sqrt(ox * ox + oy * oy)
state.offsetX = ox
state.offsetY = oy
state.targetDistance = dist
if inner and inner:IsA("GuiObject") then
state.isInSuccessZone = dist < inner.Size.X.Scale / 2
end
return state
end
local function stopMinigame()
minigame.active = false
if minigame.conn then
minigame.conn:Disconnect()
minigame.conn = nil
end
local t = minigame.thread
minigame.thread = nil
if t and t ~= coroutine.running() then
pcall(task.cancel, t)
end
releaseAllKeys()
end
local function startMinigame(rod, seed)
if minigame.active or not Config.Enabled then
return
end
minigame.active = true
minigame.seed = seed
minigame.startedAt = tick()
minigame.guiSeen = false
setState("Minigame Active")
minigame.thread = task.spawn(function()
local lastClick = 0
local warned = false
while minigame.active and Config.Enabled do
local ok, err = pcall(function()
local s = getMinigameState()
steerKeys(s.offsetX, s.offsetY)
local now = tick()
if s.isInSuccessZone
and now - lastClick >= Config.MinigameClickRate
and rod.Parent == LocalPlayer.Character then
activateRod(rod)
lastClick = now
end
end)
if not ok and not warned then
warned = true
warn("[AutoFishing] minigame step error: " .. tostring(err))
end
task.wait(0.03)
end
stopMinigame()
end)
minigame.conn = RunService.RenderStepped:Connect(function()
local pg = LocalPlayer:FindFirstChild("PlayerGui")
local frame = pg and pg:FindFirstChild("FishMinigame")
if frame then
minigame.guiSeen = true
elseif minigame.guiSeen or tick() - minigame.startedAt > 3 then
stopMinigame()
setState("Minigame ended...")
end
end)
end
local function disconnectEvent()
if eventConn then
eventConn:Disconnect()
eventConn = nil
end
end
local function fishCycle(rod, remote)
local outcome = nil
disconnectEvent()
eventConn = remote.OnClientEvent:Connect(function(data)
if not Config.Enabled or type(data) ~= "table" then
return
end
local kind = data.type
if kind == "BITE" then
setState("Bite! Hooking...")
task.delay(Config.AutoHookDelay, function()
if Config.Enabled and rod.Parent == LocalPlayer.Character then
activateRod(rod)
end
end)
elseif kind == "MINIGAME" then
setState("Starting Minigame...")
local seed = data.seed
task.delay(0.1, function()
startMinigame(rod, seed)
end)
elseif kind == "LAND" then
setState("Caught: " .. tostring(data.name or "Fish") .. "!")
stopMinigame()
outcome = "LAND"
elseif kind == "BITE_ESCAPE" then
setState("Fish escaped...")
stopMinigame()
outcome = "ESCAPE"
elseif kind == "RESET" then
setState("Resetting...")
stopMinigame()
outcome = "RESET"
end
end)
setState("Casting...")
activateRod(rod)
task.wait(Config.CastStrength)
setState("Waiting for fish...")
local waitStart = tick()
local died = false
while Config.Enabled and not outcome and tick() - waitStart < CAST_TIMEOUT do
if not isAlive() then
died = true
break
end
if minigame.active then
waitStart = tick()
end
task.wait(0.5)
end
disconnectEvent()
stopMinigame()
if Config.Enabled and not died then
if outcome == "LAND" then
task.wait(0.5)
resetRod(rod)
elseif outcome == nil then
resetRod(rod)
end
setState("Preparing next...")
task.wait(Config.AutoCastDelay)
end
end
local function runIteration()
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if not hum or hum.Health <= 0 then
setState("Waiting for character...")
task.wait(1)
return
end
local rod = findRod()
if not rod then
setState("Searching for rod...")
task.wait(1)
return
end
if not equipRod(rod) then
setState("Equipping rod...")
task.wait(1)
return
end
local remote = rod:FindFirstChild("ClassEvent")
if remote and remote:IsA("RemoteEvent") then
fishCycle(rod, remote)
else
setState("Basic mode...")
activateRod(rod)
task.wait(3)
end
end
local function mainLoop()
while Config.Enabled do
local ok, err = pcall(runIteration)
if not ok then
warn("[AutoFishing] error: " .. tostring(err))
disconnectEvent()
stopMinigame()
setState("Error - retrying...")
task.wait(1)
end
end
end
local function startLoop()
if loopThread and coroutine.status(loopThread) ~= "dead" then
return
end
loopThread = task.spawn(mainLoop)
end
local function stopLoop()
local t = loopThread
loopThread = nil
if t and t ~= coroutine.running() then
pcall(task.cancel, t)
end
disconnectEvent()
stopMinigame()
releaseAllKeys()
currentState = "Idle"
updateUI()
end
local function setEnabled(on)
Config.Enabled = on and true or false
if Config.Enabled then
startLoop()
else
stopLoop()
end
updateUI()
end
local function cycleStrength()
local idx = table.find(Config.StrengthOrder, Config.CurrentMode) or 0
Config.CurrentMode = Config.StrengthOrder[idx % #Config.StrengthOrder + 1]
Config.CastStrength = Config.StrengthModes[Config.CurrentMode]
updateUI()
end
track(toggleBtn.Activated:Connect(function() setEnabled(not Config.Enabled) end))
track(miniToggle.Activated:Connect(function() setEnabled(not Config.Enabled) end))
track(strengthBtn.Activated:Connect(cycleStrength))
track(settingsBtn.Activated:Connect(function()
settingsFrame.Visible = not settingsFrame.Visible
settingsBtn.Text = settingsFrame.Visible and "Settings [-]" or "Settings [+]"
end))
track(invertBtn.Activated:Connect(function()
Config.InvertKeys = not Config.InvertKeys
updateUI()
end))
track(minimizeBtn.Activated:Connect(function() setMinimized(true) end))
track(miniExpand.Activated:Connect(function() setMinimized(false) end))
track(UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then
return
end
if input.KeyCode == Enum.KeyCode.F then
setEnabled(not Config.Enabled)
elseif input.KeyCode == Enum.KeyCode.G then
cycleStrength()
elseif input.KeyCode == Enum.KeyCode.RightControl then
setMinimized(panel.Visible)
end
end))
track(LocalPlayer.CharacterAdded:Connect(function()
stopLoop()
if Config.Enabled then
task.wait(2)
if Config.Enabled then
startLoop()
end
end
end))
env.__AutoFishingCleanup = function()
Config.Enabled = false
stopLoop()
for _, conn in ipairs(connections) do
pcall(function() conn:Disconnect() end)
end
connections = {}
gui:Destroy()
end
updateUI()


