
Keyless
if getgenv().AutoPoopCleanup then
pcall(getgenv().AutoPoopCleanup)
getgenv().AutoPoopCleanup = nil
end
if getgenv().AutoPoopLibrary then
pcall(function()
getgenv().AutoPoopLibrary:Unload()
end)
getgenv().AutoPoopLibrary = nil
end
local libraryUrl = "https://raw.githubusercontent.com/croksyscript/CrypticLoader/refs/heads/main/Obsidian/Obsidian_UI/Obsidian_UI.lua"
local success, Library = pcall(function()
return loadstring(game:HttpGet(libraryUrl))()
end)
if not success or not Library then
return
end
getgenv().AutoPoopLibrary = Library
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local GuiService = game:GetService("GuiService")
local RunService = game:GetService("RunService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local LocalPlayer = Players.LocalPlayer
local PlayerScripts = LocalPlayer:WaitForChild("PlayerScripts")
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
local Packets = require(ReplicatedStorage:WaitForChild("Packets"))
local minigameActive = PlayerScripts:WaitForChild("IS_MINIGAME_ACTIVE")
local okFood, FoodData = pcall(require, ReplicatedStorage:WaitForChild("FoodData"))
local okMoney, FormatMoney = pcall(require, ReplicatedStorage:WaitForChild("FormatMoney"))
local PoopUI = PlayerGui:WaitForChild("PoopUI")
local lineTrigger = PoopUI:WaitForChild("CatchBar"):WaitForChild("LineTrigger")
local slider = PoopUI.CatchBar:WaitForChild("BarLine"):WaitForChild("Slider")
local target = slider:WaitForChild("Target")
local BACKPACK_LIMIT = 300
local state = {
running = true,
notifications = {},
remotePoop = false,
legitPoop = false,
instantLine = false,
remoteDelay = 1,
pause = 2,
cycles = 0,
autoSell = false,
sellMode = "Poop Count",
sellValue = 100,
sellAction = "Sell All",
sellOneMode = "First",
lastSell = os.clock(),
autoBuy = false,
buyInterval = 10,
selectedFoods = {},
stock = {},
}
local connections = {}
local function jitter(minValue, maxValue)
return minValue + math.random() * (maxValue - minValue)
end
local function formatMoney(value)
if okMoney and typeof(value) == "number" then
local ok, text = pcall(FormatMoney, value, true)
if ok then
return text
end
end
return tostring(value)
end
local function isPoop(tool)
return tool:IsA("Tool") and tool.Name:sub(1, 4) == "Poop"
end
local function getPoops()
local list = {}
for _, tool in ipairs(LocalPlayer.Backpack:GetChildren()) do
if isPoop(tool) then
table.insert(list, tool)
end
end
local character = LocalPlayer.Character
local held = character and character:FindFirstChildOfClass("Tool")
if held and isPoop(held) then
table.insert(list, held)
end
return list
end
local function getBackpackSize()
return #LocalPlayer.Backpack:GetChildren()
end
local function getWeight(tool)
return tonumber(tool.Name:match("([%d%.]+)%s*lb")) or 0
end
local CLICK_LATENCY = 0.05
local lastCenter = 0
local lastTime = os.clock()
local function isLineInTarget()
local now = os.clock()
local center = lineTrigger.AbsolutePosition.X + lineTrigger.AbsoluteSize.X / 2
local elapsed = math.max(now - lastTime, 1 / 240)
local velocity = (center - lastCenter) / elapsed
lastCenter = center
lastTime = now
local predicted = center + velocity * CLICK_LATENCY
local margin = lineTrigger.AbsoluteSize.X * 0.065
local left = target.AbsolutePosition.X
return left - margin < predicted and predicted < left + target.AbsoluteSize.X + margin
end
local function sendMouse(position, down)
VirtualInputManager:SendMouseButtonEvent(position.X, position.Y, 0, down, game, 0)
end
local CLICK_SPOTS = {}
for _, x in ipairs({ 0.04, 0.96, 0.08, 0.92 }) do
for _, y in ipairs({ 0.35, 0.5, 0.25, 0.65, 0.75 }) do
table.insert(CLICK_SPOTS, Vector2.new(x, y))
end
end
local function isSpotFree(point)
local inset = GuiService:GetGuiInset()
for _, gui in ipairs(PlayerGui:GetGuiObjectsAtPosition(point.X - inset.X, point.Y - inset.Y)) do
if not gui:IsDescendantOf(PoopUI) and (gui:IsA("GuiButton") or gui.Active) then
return false
end
end
return true
end
local function getFreePoint()
local size = workspace.CurrentCamera.ViewportSize
for _, spot in ipairs(CLICK_SPOTS) do
local point = size * spot
if isSpotFree(point) then
return point
end
end
return nil
end
local function clickTrigger()
local position = getFreePoint()
if not position then
return
end
sendMouse(position, true)
task.wait(0.03)
sendMouse(position, false)
end
local function getChargeFill()
local chargeUi = workspace:FindFirstChild("ChargeUI")
local bar = chargeUi and chargeUi:FindFirstChild("BarProgress", true)
return bar and bar:FindFirstChild("Fill")
end
local function chargeBar()
local position = getFreePoint()
if not position then
task.wait(1)
return
end
sendMouse(position, true)
local startTime = os.clock()
local fill
repeat
task.wait()
fill = getChargeFill()
until fill or os.clock() - startTime > 0.5
while os.clock() - startTime < 2 do
if fill and fill.Parent and fill.Size.Y.Scale >= 0.97 then
break
end
if not fill and os.clock() - startTime >= 0.7 then
break
end
task.wait()
end
sendMouse(position, false)
end
local targetAspect = target:FindFirstChildOfClass("UIAspectRatioConstraint")
local defaultAspectRatio = targetAspect and targetAspect.AspectRatio
local WIDE_ASPECT_RATIO = 4
local function moveTargetToLine()
local lineCenter = lineTrigger.AbsolutePosition.X + lineTrigger.AbsoluteSize.X / 2
local targetCenter = target.AbsolutePosition.X + target.AbsoluteSize.X / 2
local position = slider.Position
slider.Position = UDim2.new(position.X.Scale, position.X.Offset + lineCenter - targetCenter, position.Y.Scale, position.Y.Offset)
end
local function widenTarget()
if targetAspect then
targetAspect.AspectRatio = WIDE_ASPECT_RATIO
end
end
local function restoreTarget()
if targetAspect then
targetAspect.AspectRatio = defaultAspectRatio
end
end
local function runLegitCycle()
if not minigameActive.Value then
chargeBar()
task.wait(0.3)
end
local deadline = os.clock() + 30
while state.running and minigameActive.Value and os.clock() < deadline do
if state.instantLine then
widenTarget()
moveTargetToLine()
RunService.Heartbeat:Wait()
moveTargetToLine()
clickTrigger()
task.wait(jitter(0.2, 0.3))
elseif isLineInTarget() then
clickTrigger()
task.wait(jitter(0.4, 0.55))
end
task.wait()
end
restoreTarget()
state.cycles += 1
task.wait(state.pause * jitter(0.85, 1.25))
end
local function runRemoteCycle()
Packets.RequestPoop:Fire()
state.cycles += 1
local delay = state.remoteDelay * jitter(0.8, 1.35)
if math.random() < 0.05 then
delay += jitter(2, 4)
end
task.wait(delay)
end
local function pickPoop(mode)
local poops = getPoops()
if #poops == 0 then
return nil
end
if mode == "Held" then
local character = LocalPlayer.Character
local held = character and character:FindFirstChildOfClass("Tool")
return held and isPoop(held) and held or nil
end
if mode == "Smallest" or mode == "Biggest" then
table.sort(poops, function(a, b)
if mode == "Smallest" then
return getWeight(a) < getWeight(b)
end
return getWeight(a) > getWeight(b)
end)
end
return poops[1]
end
local function sellOne(mode)
local tool = pickPoop(mode)
local uuid = tool and tool:GetAttribute("uniquePoopID")
if not uuid then
return false
end
local ok, result = pcall(function()
return Packets.SellCurrentlyHeldPoop:Fire(uuid)
end)
if ok then
Library:Notify("Sold poop for " .. formatMoney(result), 3)
end
return ok
end
local function sellAll()
local ok, result = pcall(function()
return Packets.SellInventory:Fire()
end)
if ok then
state.lastSell = os.clock()
Library:Notify("Sold inventory for " .. formatMoney(result), 3)
end
return ok
end
local function runSell()
if state.sellAction == "Sell One" then
return sellOne(state.sellOneMode == "Held" and "First" or state.sellOneMode)
end
return sellAll()
end
local function refreshStock()
pcall(function()
Packets.FoodStock:Fire()
end)
end
local function buyFood(name)
pcall(function()
Packets.PurchaseFood:Fire(name)
end)
end
local function getFoodNames()
local names = {}
if okFood and typeof(FoodData) == "table" then
for name in pairs(FoodData) do
table.insert(names, name)
end
table.sort(names, function(a, b)
return FoodData[a].Price < FoodData[b].Price
end)
end
return names
end
local function getStockText()
local names = {}
for name in pairs(state.stock) do
table.insert(names, name)
end
table.sort(names)
return #names > 0 and table.concat(names, ", ") or "Nothing"
end
table.insert(connections, Packets.FoodStock.OnClientEvent:Connect(function(list)
state.stock = {}
if typeof(list) == "table" then
for _, name in ipairs(list) do
state.stock[name] = true
end
end
end))
table.insert(connections, Packets.PurchaseFood.OnClientEvent:Connect(function(purchased, name)
if purchased then
state.stock[name] = nil
table.insert(state.notifications, "Bought " .. tostring(name))
end
end))
task.spawn(function()
while state.running do
local hasSpace = getBackpackSize() < BACKPACK_LIMIT
local cycle = nil
if hasSpace and state.remotePoop then
cycle = runRemoteCycle
elseif state.legitPoop and (hasSpace or minigameActive.Value) then
cycle = runLegitCycle
end
if cycle then
local ok = pcall(cycle)
if not ok then
task.wait(1)
end
else
task.wait(0.3)
end
end
end)
task.spawn(function()
while state.running do
if state.autoSell then
if state.sellMode == "Seconds" then
if os.clock() - state.lastSell >= state.sellValue then
state.lastSell = os.clock()
runSell()
end
elseif #getPoops() >= state.sellValue then
runSell()
task.wait(2)
end
end
task.wait(0.5)
end
end)
task.spawn(function()
while state.running do
local text = table.remove(state.notifications, 1)
if text then
Library:Notify(text, 3)
end
task.wait(0.3)
end
end)
local lastBuyCheck = 0
task.spawn(function()
while state.running do
local due = os.clock() - lastBuyCheck >= state.buyInterval
if state.autoBuy and due and next(state.selectedFoods) then
lastBuyCheck = os.clock()
refreshStock()
task.wait(1)
for name in pairs(state.selectedFoods) do
if state.stock[name] then
buyFood(name)
task.wait(jitter(0.5, 0.9))
end
end
end
task.wait(0.5)
end
end)
local Window = Library:CreateWindow({
Title = "Open Source - Poop a Big Poop",
Footer = ".gg/XDpsSW7Ybs",
Icon = 120217226033572,
NotifySide = "Right",
Center = true,
AutoShow = true,
ToggleKeybind = Enum.KeyCode.RightControl,
Folder = "Poop",
})
local function buildPoopTab(Tab)
local Box = Tab:AddGroupbox({
Side = "Left",
Name = "Auto Poop",
IconName = "zap",
})
Box:AddToggle("RemotePoopToggle", {
Text = "Auto Poop (Remote)",
Default = false,
Tooltip = "Fast, sends the poop request directly",
Callback = function(value)
state.remotePoop = value
if value and Library.Toggles.LegitPoopToggle.Value then
Library.Toggles.LegitPoopToggle:SetValue(false)
end
end,
})
Box:AddSlider("RemoteDelay", {
Text = "Remote Delay",
Default = 1,
Min = 0.3,
Max = 5,
Rounding = 1,
Suffix = "s",
Tooltip = "Lower values are faster but riskier",
Callback = function(value)
state.remoteDelay = value
end,
})
Box:AddDivider("Legit")
Box:AddToggle("LegitPoopToggle", {
Text = "Auto Poop (Legit)",
Default = false,
Tooltip = "Charges the bar and clicks the minigame like a player",
Callback = function(value)
state.legitPoop = value
if value and Library.Toggles.RemotePoopToggle.Value then
Library.Toggles.RemotePoopToggle:SetValue(false)
end
end,
})
Box:AddToggle("InstantLineToggle", {
Text = "Instant Line",
Default = false,
Tooltip = "Moves the target under the line before every click",
Callback = function(value)
state.instantLine = value
end,
})
Box:AddSlider("PoopPause", {
Text = "Legit Pause",
Default = 2,
Min = 0.1,
Max = 10,
Rounding = 1,
Suffix = "s",
Callback = function(value)
state.pause = value
end,
})
local Info = Tab:AddGroupbox({
Side = "Right",
Name = "Info",
IconName = "info",
})
local statusLabel = Info:AddLabel("Poops: 0")
local cycleLabel = Info:AddLabel("Poops Made: 0")
local sizeLabel = Info:AddLabel("Inventory: 0 / " .. BACKPACK_LIMIT)
task.spawn(function()
while state.running do
statusLabel:SetText("Poops: " .. #getPoops())
cycleLabel:SetText("Poops Made: " .. state.cycles)
sizeLabel:SetText(("Inventory: %d / %d"):format(getBackpackSize(), BACKPACK_LIMIT))
task.wait(0.5)
end
end)
end
local function buildSellTab(Tab)
local Manual = Tab:AddGroupbox({
Side = "Left",
Name = "Manual Sell",
IconName = "hand-coins",
})
Manual:AddButton({
Text = "Sell All Poops",
Func = sellAll,
})
Manual:AddDropdown("SellOneMode", {
Text = "Sell One Mode",
Values = { "First", "Smallest", "Biggest", "Held" },
Default = 1,
Callback = function(value)
state.sellOneMode = value
end,
})
Manual:AddButton({
Text = "Sell One Poop",
Func = function()
if not sellOne(state.sellOneMode) then
Library:Notify("No matching poop found", 3)
end
end,
})
local Auto = Tab:AddGroupbox({
Side = "Right",
Name = "Auto Sell",
IconName = "repeat",
})
Auto:AddToggle("AutoSellToggle", {
Text = "Auto Sell",
Default = false,
Callback = function(value)
state.autoSell = value
state.lastSell = os.clock()
end,
})
Auto:AddDropdown("AutoSellMode", {
Text = "Sell Trigger",
Values = { "Poop Count", "Seconds" },
Default = 1,
Callback = function(value)
state.sellMode = value
end,
})
Auto:AddInput("AutoSellValue", {
Text = "Poops or Seconds",
Default = "100",
Numeric = true,
Finished = true,
Placeholder = "100",
Callback = function(value)
state.sellValue = math.max(tonumber(value) or 1, 1)
end,
})
Auto:AddDropdown("AutoSellAction", {
Text = "Sell Action",
Values = { "Sell All", "Sell One" },
Default = 1,
Callback = function(value)
state.sellAction = value
end,
})
Auto:AddLabel({
Text = "Stand near the sell NPC",
DoesWrap = true,
})
end
local function buildFoodTab(Tab)
local Buy = Tab:AddGroupbox({
Side = "Left",
Name = "Buy Food",
IconName = "shopping-cart",
})
Buy:AddDropdown("FoodSelect", {
Text = "Foods",
Values = getFoodNames(),
Default = {},
Multi = true,
AllowNull = true,
SelectAllButtons = true,
Callback = function(values)
state.selectedFoods = {}
for name, enabled in pairs(values) do
if enabled then
state.selectedFoods[name] = true
end
end
end,
})
Buy:AddButton({
Text = "Buy Selected Once",
Func = function()
refreshStock()
task.wait(1)
for name in pairs(state.selectedFoods) do
if state.stock[name] then
buyFood(name)
task.wait(jitter(0.5, 0.9))
else
Library:Notify(name .. " is out of stock", 3)
end
end
end,
})
Buy:AddButton({
Text = "Buy Everything In Stock",
Func = function()
refreshStock()
task.wait(1)
for name in pairs(state.stock) do
buyFood(name)
task.wait(jitter(0.5, 0.9))
end
end,
})
local Auto = Tab:AddGroupbox({
Side = "Right",
Name = "Auto Buy",
IconName = "repeat",
})
Auto:AddToggle("AutoBuyToggle", {
Text = "Auto Buy Selected",
Default = false,
Callback = function(value)
state.autoBuy = value
end,
})
Auto:AddSlider("BuyInterval", {
Text = "Check Stock Every",
Default = 10,
Min = 5,
Max = 60,
Rounding = 0,
Suffix = "s",
Callback = function(value)
state.buyInterval = value
end,
})
local stockLabel = Auto:AddLabel({
Text = "In Stock: Nothing",
DoesWrap = true,
})
task.spawn(function()
while state.running do
stockLabel:SetText("In Stock: " .. getStockText())
task.wait(1)
end
end)
refreshStock()
end
local Tabs = {
Poop = Window:AddTab("Poop", "zap"),
Sell = Window:AddTab("Sell", "hand-coins"),
Food = Window:AddTab("Food", "shopping-cart"),
}
buildPoopTab(Tabs.Poop)
buildSellTab(Tabs.Sell)
buildFoodTab(Tabs.Food)
local function cleanup()
state.running = false
state.remotePoop = false
state.legitPoop = false
state.autoSell = false
state.autoBuy = false
restoreTarget()
for _, connection in ipairs(connections) do
connection:Disconnect()
end
connections = {}
end
getgenv().AutoPoopCleanup = cleanup
Library:OnUnload(cleanup)No comments yet for this script.