Myros27

disentchantmentBot V2

May 20th, 2025
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 24.58 KB | None | 0 0
  1. --[[
  2.     DisenchantBot v1.2 - No Movement Library, Refined Cycle Logic
  3.     Uses direct turtle API for movement.
  4.     Adjusted disenchanting steps for item processing and final count.
  5. ]]
  6.  
  7. --#region Configuration
  8. local CHAT_BOX_PERIPHERAL_NAME = "chatBox"
  9. local COMMAND_PREFIX = "@disenchant"
  10. local CHAT_BOT_NAME = "DisenchantBot"
  11. local CHAT_BOT_BRACKETS = "[]"
  12. local CHAT_BOT_BRACKET_COLOR = "&3" -- Cyan
  13.  
  14. local DEBUG_LOGGING_ENABLED = true
  15. local LOG_FILE_NAME = "disenchant.log"
  16.  
  17. local AUTOMATA_PERIPHERAL_NAME = "endAutomata" -- For goHome()
  18. --#endregion
  19.  
  20. --#region Global State
  21. local chatBox = nil
  22. local chatBoxFunctional = true
  23. local isDisenchantRunning = false
  24. local fuelWarningSent = false -- Kept for general fuel awareness
  25. local automataPeripheral = nil -- Store the automata peripheral
  26. --#endregion
  27.  
  28. --#region Logger Setup
  29. local function writeToLogFile(message) if not DEBUG_LOGGING_ENABLED then return end; local f,e=fs.open(LOG_FILE_NAME,"a"); if f then f.write(string.format("[%s] %s\n",os.date("%Y-%m-%d %H:%M:%S"),message));f.close() else print("LOG ERR: "..tostring(e))end end
  30. local function botLog(msg)local fm="["..CHAT_BOT_NAME.."-Debug] "..msg; print(fm); writeToLogFile(fm)end
  31. --#endregion
  32.  
  33. --#region Basic Turtle Functions (No ML)
  34. local function goHome()
  35.     if not automataPeripheral then
  36.         automataPeripheral = peripheral.find(AUTOMATA_PERIPHERAL_NAME)
  37.         if not automataPeripheral then
  38.             botLog("CRITICAL WARN: EndAutomata peripheral ('"..AUTOMATA_PERIPHERAL_NAME.."') not found. Cannot warp home.")
  39.             return false
  40.         end
  41.     end
  42.     botLog("Warping home via EndAutomata...")
  43.     local success, reason = pcall(function() return automataPeripheral.warpToPoint("home") end)
  44.     if success and reason then -- pcall returns true for success, 'reason' is the actual return value of warpToPoint
  45.         botLog("Warp home successful.")
  46.         -- Assuming home position sets turtle facing North (standard for many setups)
  47.         return true
  48.     else
  49.         botLog("Failed to warp home: " .. tostring(reason or "pcall error"))
  50.         return false
  51.     end
  52. end
  53. --#endregion
  54.  
  55. --#region DisenchantBot Peripherals and Helpers
  56. local function sendFormattedChat(messageComponents)
  57.     local plainText = ""
  58.     if type(messageComponents) == "table" then
  59.         for _,c_comp in ipairs(messageComponents) do plainText = plainText .. (type(c_comp) == "table" and c_comp.text or tostring(c_comp) or "") end
  60.     elseif messageComponents ~= nil then plainText = tostring(messageComponents)
  61.     else plainText = "nil_message_components_to_sendFormattedChat" end
  62.  
  63.     if not chatBoxFunctional or not chatBox then botLog("[NoChat] " .. plainText); return false end
  64.  
  65.     local jsonMessage_success, jsonMessage = pcall(textutils.serialiseJSON, messageComponents)
  66.     if not jsonMessage_success then botLog("!!! textutils.serialiseJSON FAILED: " .. tostring(jsonMessage)); print("ERROR: Could not serialize chat message!"); return false end
  67.  
  68.     local peripheral_call_success, peripheral_error = pcall(function() return chatBox.sendFormattedMessage(jsonMessage, CHAT_BOT_NAME, CHAT_BOT_BRACKETS, CHAT_BOT_BRACKET_COLOR) end)
  69.     if not peripheral_call_success then botLog("!!! chatBox.sendFormattedMessage FAILED: " .. tostring(peripheral_error)); print("ERROR: Failed to send formatted message via chatBox: " .. tostring(peripheral_error)); return false end
  70.     return true
  71. end
  72.  
  73. local function announce(messageComponents)
  74.     if not sendFormattedChat(messageComponents) then botLog("Announce: sendFormattedChat failed for: " .. textutils.serialize(messageComponents, {compact=true, max_depth=2})) end
  75.     sleep(0.3)
  76. end
  77.  
  78. local COLORS={GOLD="gold",AQUA="aqua",GRAY="gray",RED="red",GREEN="green",YELLOW="yellow",WHITE="white",DARK_RED="dark_red",DARK_GRAY="dark_gray"}
  79.  
  80. local function critical_error_handler(message, doTurnRightForThisError) -- Changed second param
  81.     announce({{text = "CRITICAL ERROR: " .. message .. " Halting " .. CHAT_BOT_NAME .. ".", c = COLORS.DARK_RED, b = true}})
  82.     botLog("CRITICAL ERROR: " .. message)
  83.     if doTurnRightForThisError then
  84.         botLog("Performing specific turnRight for this error.")
  85.         turtle.turnRight()
  86.     end
  87.     isDisenchantRunning = false
  88. end
  89.  
  90. local function check_stop_signal()
  91.     if not isDisenchantRunning then botLog("Stop signal detected during cycle."); return true end
  92.     sleep(0) -- Yield
  93.     return false
  94. end
  95. --#endregion
  96.  
  97. --#region DisenchantBot Command Handlers
  98. local commandHandlers = {}
  99. commandHandlers.help = function(u,_)
  100.     announce({{text="--- "..CHAT_BOT_NAME.." Cmds ("..COMMAND_PREFIX..") ---",c=COLORS.GOLD,b=true}})
  101.     announce({{text=COMMAND_PREFIX.." help",c=COLORS.AQUA},{text=" - Shows this help message.",c=COLORS.GRAY}})
  102.     announce({{text=COMMAND_PREFIX.." start",c=COLORS.AQUA},{text=" - Starts the disenchanting loop.",c=COLORS.GRAY}})
  103.     announce({{text=COMMAND_PREFIX.." stop",c=COLORS.AQUA},{text=" - Stops the current disenchanting loop.",c=COLORS.GRAY}})
  104.     announce({{text=COMMAND_PREFIX.." cmd <lua_code>",c=COLORS.AQUA},{text=" - Executes Lua code (e.g., turtle.forward()).",c=COLORS.GRAY}})
  105.     announce({{text=COMMAND_PREFIX.." fuel",c=COLORS.AQUA},{text=" - Shows current fuel level.",c=COLORS.GRAY}})
  106.     announce({{text=COMMAND_PREFIX.." unstuck",c=COLORS.AQUA},{text=" - Tries to warp home (stops current cycle).",c=COLORS.GRAY}})
  107. end
  108.  
  109. commandHandlers.cmd = function(u,a)
  110.     if #a == 0 then announce({{text="Usage: "..COMMAND_PREFIX.." cmd <lua_code_to_run>",c=COLORS.YELLOW}}); return end
  111.     if #a == 1 and string.lower(a[1]) == "help" then
  112.         announce({{text="--- "..COMMAND_PREFIX.." cmd Help ---",c=COLORS.GOLD,b=true}})
  113.         announce({{text="Usage: "..COMMAND_PREFIX.." cmd <lua_code_to_run>",c=COLORS.WHITE}})
  114.         announce({{text="Executes Lua code. Examples:",c=COLORS.AQUA}})
  115.         announce({{text="  "..COMMAND_PREFIX.." cmd turtle.turnRight()",c=COLORS.WHITE}})
  116.         announce({{text="  "..COMMAND_PREFIX.." cmd return turtle.getFuelLevel()",c=COLORS.WHITE}})
  117.         return
  118.     end
  119.     local cs=table.concat(a," "); botLog("User "..u.." executing raw command: "..cs)
  120.     local fn,e=load("return "..cs,"raw_user_command","t",{turtle=turtle, peripheral=peripheral, os=os, fs=fs, textutils=textutils, sleep=sleep, print=print, announce=announce, COLORS=COLORS, goHome=goHome})
  121.     if not fn then announce({{text="Error parsing cmd: ",c=COLORS.RED},{text=tostring(e),c=COLORS.YELLOW}}); botLog("Parse error for raw cmd '"..cs.."': "..tostring(e)); return end
  122.     local s,r=pcall(fn)
  123.     if s then announce({{text="Cmd executed. Result: ",c=COLORS.GREEN},{text=tostring(r),c=COLORS.WHITE}}); botLog("Raw cmd '"..cs.."' result: "..tostring(r))
  124.     else announce({{text="Error executing cmd: ",c=COLORS.RED},{text=tostring(r),c=COLORS.YELLOW}}); botLog("Execution error for raw cmd '"..cs.."': "..tostring(r)) end
  125. end
  126.  
  127. commandHandlers.fuel = function(u,_) local l,lm=turtle.getFuelLevel(),turtle.getFuelLimit(); announce({{text="Fuel: ",c=COLORS.AQUA},{text=tostring(l)..(lm>0 and(" / "..tostring(lm))or" (Unlimited)"),c=COLORS.WHITE}}) end
  128. commandHandlers.unstuck = function(u,_)
  129.     announce({{text="Unstuck: Attempting to warp home...",c=COLORS.YELLOW}});botLog(u.." used unstuck command.")
  130.     isDisenchantRunning = false
  131.     if goHome()then announce({{text="Warped home successfully.",c=COLORS.GREEN}}) else announce({{text="Failed to warp home.",c=COLORS.RED}})end
  132. end
  133. --#endregion
  134.  
  135. --#region DisenchantBot Core Logic
  136. function performSingleDisenchantCycle()
  137.     botLog("== Starting new Disenchant Cycle ==")
  138.     if check_stop_signal() then return "stopped_by_user" end
  139.  
  140.     botLog("Step 1: Warping home.")
  141.     if not goHome() then critical_error_handler("Failed to warp home at cycle start.", false); return "error" end
  142.     -- Assuming home faces North.
  143.     if check_stop_signal() then return "stopped_by_user" end
  144.  
  145.     botLog("Step 2: Dropping all inventory items (slots 1-16).")
  146.     for i = 1, 16 do turtle.select(i); if turtle.getItemCount(i) > 0 then botLog("Dropping from slot " .. i); turtle.drop() end end
  147.     if check_stop_signal() then return "stopped_by_user" end
  148.  
  149.     botLog("Step 3: Checking fuel.")
  150.     local fuelLevel = turtle.getFuelLevel(); local fuelLimit = turtle.getFuelLimit()
  151.     if fuelLimit > 0 and (fuelLevel / fuelLimit) < 0.50 then
  152.         announce({{text="Fuel under 50% ("..fuelLevel.."/"..fuelLimit.."). Refueling...", c=COLORS.YELLOW}})
  153.         botLog("Attempting to suck, refuel, drop excess from below.")
  154.         turtle.select(1) -- Use slot 1 for fuel operations
  155.         if not turtle.suck() then announce({{text="Failed to suck fuel item from below.", c=COLORS.RED}}); botLog("Failed to suck fuel from below.")
  156.         else
  157.             botLog("Sucked an item, attempting to refuel.")
  158.             if not turtle.refuel() then announce({{text="Refuel failed. Item might not be fuel.", c=COLORS.RED}}); botLog("turtle.refuel() failed.")
  159.             else botLog("Refuel successful. Fuel: " .. turtle.getFuelLevel()) end
  160.             turtle.drop() -- Drop remaining/unburnable item
  161.             botLog("Dropped item from slot 1 after refuel attempt.")
  162.         end
  163.     else botLog("Fuel OK ("..fuelLevel.."/"..fuelLimit.."). Skipping refuel.") end
  164.     if check_stop_signal() then return "stopped_by_user" end
  165.  
  166.     botLog("Step 4: Turning right (to face East - Input Chest).")
  167.     turtle.turnRight()
  168.     if check_stop_signal() then return "stopped_by_user" end
  169.  
  170.     botLog("Step 5: Sucking up to 16 items (1 by 1) from Input Chest (East).")
  171.     local itemsSuckedInStep5_ops = 0
  172.     for i = 1, 16 do
  173.         local emptySlotFound = false; local originalSelection = turtle.getSelectedSlot()
  174.         for slotIdx = 1, 16 do if turtle.getItemCount(slotIdx) == 0 then turtle.select(slotIdx); emptySlotFound = true; break end end
  175.         if not emptySlotFound then botLog("No empty slot to suck item into."); turtle.select(originalSelection); break end
  176.        
  177.         if turtle.suck(1) then itemsSuckedInStep5_ops = itemsSuckedInStep5_ops + 1; botLog("Sucked item op " .. itemsSuckedInStep5_ops .. " into slot " .. turtle.getSelectedSlot())
  178.         else turtle.select(originalSelection); botLog("Failed to suck more from Input Chest after " .. itemsSuckedInStep5_ops .. " ops."); break end
  179.     end
  180.     announce({{text="Sucked "..itemsSuckedInStep5_ops.." item(s)/stacks from Input Chest.", c=COLORS.GRAY}})
  181.     if check_stop_signal() then return "stopped_by_user" end
  182.  
  183.     botLog("Step 6: Verifying sucked items and counting non-stackables.")
  184.     local slotsWithValidNonStackableItems = {}
  185.     for slot = 1, 16 do
  186.         turtle.select(slot)
  187.         if turtle.getItemCount(slot) > 0 then
  188.             local space = turtle.getItemSpace(slot)
  189.             if space == 0 then table.insert(slotsWithValidNonStackableItems, slot); botLog("Slot " .. slot .. ": Non-stackable item found. Name: " .. (turtle.getItemDetail() and turtle.getItemDetail().name or "N/A"))
  190.             elseif space == 64 then critical_error_handler("Inv check contradiction: Slot " .. slot .. " has items but getItemSpace is 64.", false); return "error" -- Error means don't turn right
  191.             else critical_error_handler("Inv check: Slot " .. slot .. " has unexpected stackable item (space=" .. space .. "). Expected only non-stackables.", false); return "error"
  192.             end
  193.         end
  194.     end
  195.     local expectedNonStackableCount = #slotsWithValidNonStackableItems
  196.     botLog("Found " .. expectedNonStackableCount .. " non-stackable item(s) to process in slots: " .. table.concat(slotsWithValidNonStackableItems, ", "))
  197.  
  198.     if expectedNonStackableCount == 0 then
  199.         if itemsSuckedInStep5_ops > 0 then announce({{text="No valid non-stackable items out of "..itemsSuckedInStep5_ops.." sucked. Restarting after delay.", c=COLORS.YELLOW}})
  200.         else announce({{text="No items found in Input Chest to process. Restarting after delay.", c=COLORS.YELLOW}}) end
  201.         botLog("Turning left (to face North) before 60s wait.")
  202.         turtle.turnLeft()
  203.         botLog("Waiting 60 seconds.")
  204.         for _=1,60 do if check_stop_signal() then return "stopped_by_user" end; sleep(1) end
  205.         return "continue_looping"
  206.     end
  207.     announce({{text="Processing "..expectedNonStackableCount.." non-stackable item(s).", c=COLORS.AQUA}})
  208.     if check_stop_signal() then return "stopped_by_user" end
  209.  
  210.     -- Step 7a: Drop items into Disenchanter (tryOutputchest)
  211.     botLog("Step 7a: Turning right (to face South - Disenchanter Input).")
  212.     turtle.turnRight()
  213.    
  214.     botLog("Dropping " .. expectedNonStackableCount .. " non-stackable items into Disenchanter.")
  215.     for _, slotIdx in ipairs(slotsWithValidNonStackableItems) do
  216.         turtle.select(slotIdx)
  217.         if turtle.getItemCount(slotIdx) > 0 then
  218.             if not turtle.drop() then critical_error_handler("Failed to drop item from slot " .. slotIdx .. " into Disenchanter.", false); return "error" end
  219.         end
  220.     end
  221.    
  222.     botLog("Waiting 5 seconds after dropping items into Disenchanter.")
  223.     for _=1,5 do if check_stop_signal() then return "stopped_by_user" end; sleep(1) end
  224.  
  225.     -- Step 7b: Extractor Loop (Processing items with Disenchanter)
  226.     local items_to_expect_back_from_disenchanter = expectedNonStackableCount
  227.     local items_retrieved_from_disenchanter_this_pass = 0
  228.     local itemsSuccessfullyProcessedOverallByDisenchanter = false
  229.  
  230.     while items_to_expect_back_from_disenchanter > 0 do
  231.         if check_stop_signal() then return "stopped_by_user" end
  232.         botLog("Disenchanter Loop: Expecting to retrieve up to " .. items_to_expect_back_from_disenchanter .. " item(s).")
  233.         items_retrieved_from_disenchanter_this_pass = 0
  234.         local slots_with_retrieved_items_this_pass = {}
  235.  
  236.         for i = 1, items_to_expect_back_from_disenchanter do
  237.             local emptySlotFound = false; local originalSelection = turtle.getSelectedSlot()
  238.             for slotIdx = 1, 16 do if turtle.getItemCount(slotIdx) == 0 then turtle.select(slotIdx); emptySlotFound = true; break end end
  239.             if not emptySlotFound then botLog("Disenchanter loop: Turtle inventory full. Cannot suck more."); turtle.select(originalSelection); break end
  240.  
  241.             if turtle.suck(1) then
  242.                 items_retrieved_from_disenchanter_this_pass = items_retrieved_from_disenchanter_this_pass + 1
  243.                 table.insert(slots_with_retrieved_items_this_pass, turtle.getSelectedSlot())
  244.                 botLog("Disenchanter loop: Sucked item " .. items_retrieved_from_disenchanter_this_pass .. " (from Disenchanter) into slot " .. turtle.getSelectedSlot())
  245.             else
  246.                 turtle.select(originalSelection)
  247.                 botLog("Disenchanter loop: Failed to suck more (Disenchanter empty after " .. items_retrieved_from_disenchanter_this_pass .. " items).")
  248.                 break
  249.             end
  250.         end
  251.        
  252.         if items_retrieved_from_disenchanter_this_pass < items_to_expect_back_from_disenchanter then
  253.             itemsSuccessfullyProcessedOverallByDisenchanter = true
  254.             local processed_this_round = items_to_expect_back_from_disenchanter - items_retrieved_from_disenchanter_this_pass
  255.             announce({{text="Disenchanter processed "..processed_this_round.." item(s). " .. items_retrieved_from_disenchanter_this_pass .. " returned/remaining.", c=COLORS.GRAY}})
  256.            
  257.             if items_retrieved_from_disenchanter_this_pass > 0 then
  258.                 botLog("Dropping the " .. items_retrieved_from_disenchanter_this_pass .. " returned items back into Disenchanter.")
  259.                 for _, slotIdx in ipairs(slots_with_retrieved_items_this_pass) do
  260.                     turtle.select(slotIdx)
  261.                     if turtle.getItemCount(slotIdx) > 0 then if not turtle.drop() then botLog("WARN: Failed to re-drop item from slot " .. slotIdx) end end
  262.                 end
  263.             end
  264.            
  265.             items_to_expect_back_from_disenchanter = items_retrieved_from_disenchanter_this_pass
  266.             if items_to_expect_back_from_disenchanter == 0 then announce({{text="All items batch processed by Disenchanter.", c=COLORS.GREEN}}); botLog("All items in batch processed."); break end
  267.            
  268.             botLog("Waiting 20 seconds before next Disenchanter check.")
  269.             for _=1,20 do if check_stop_signal() then return "stopped_by_user" end; sleep(1) end
  270.         else
  271.             announce({{text="Disenchanter did not process " .. items_retrieved_from_disenchanter_this_pass .. " item(s). Assuming unprocessable.", c=COLORS.YELLOW}})
  272.             botLog("Disenchanter loop: No items lost. These " .. items_retrieved_from_disenchanter_this_pass .. " items remain in turtle.")
  273.             -- These items are now in the turtle and considered "unprocessable originals".
  274.             break
  275.         end
  276.     end
  277.     if check_stop_signal() then return "stopped_by_user" end
  278.  
  279.     -- Step 8: Conditional Wait & Turn
  280.     if itemsSuccessfullyProcessedOverallByDisenchanter then
  281.         botLog("Step 8: Items processed by Disenchanter. Waiting 15 seconds.")
  282.         for _=1,15 do if check_stop_signal() then return "stopped_by_user" end; sleep(1) end
  283.     else botLog("Step 8: No items processed by Disenchanter. Skipping 15s wait.") end
  284.    
  285.     botLog("Turning right (to face West - Disenchanter Output Chest).")
  286.     turtle.turnRight()
  287.     if check_stop_signal() then return "stopped_by_user" end
  288.  
  289.     -- Step 9: Suck 17x from Disenchanter Output Chest (West)
  290.     botLog("Step 9: Sucking 17x from Disenchanter Output Chest (West).")
  291.     local suck_ops_step9 = 0
  292.     for i = 1, 17 do
  293.         local emptySlotFound = false; local originalSelection = turtle.getSelectedSlot()
  294.         for slotIdx = 1, 16 do if turtle.getItemCount(slotIdx) == 0 then turtle.select(slotIdx); emptySlotFound = true; break end end
  295.         if not emptySlotFound then botLog("Output suck: Inventory full."); turtle.select(originalSelection); break end
  296.  
  297.         if turtle.suck() then suck_ops_step9 = suck_ops_step9 + 1; botLog("Output suck op "..i.." successful into slot " .. turtle.getSelectedSlot())
  298.         else turtle.select(originalSelection); botLog("Output suck: Chest empty after " .. suck_ops_step9 .. " ops."); break end
  299.         sleep(0.1)
  300.     end
  301.     botLog("Finished sucking from Disenchanter Output. Performed " .. suck_ops_step9 .. " suck operations.")
  302.     if check_stop_signal() then return "stopped_by_user" end
  303.  
  304.     -- Step 10: Final Inventory Check (Non-stackable items count)
  305.     botLog("Step 10: Final inventory check (expecting " .. expectedNonStackableCount .. " non-stackable items total).")
  306.     local finalNonStackableItemsInTurtle = 0
  307.     for slot = 1, 16 do
  308.         turtle.select(slot)
  309.         if turtle.getItemCount(slot) > 0 then
  310.             if turtle.getItemSpace(slot) == 0 then finalNonStackableItemsInTurtle = finalNonStackableItemsInTurtle + 1
  311.             else botLog("Slot " .. slot .. " has stackable item. Name: " .. (turtle.getItemDetail() and turtle.getItemDetail().name or "N/A") .. ". Not counted.") end
  312.         end
  313.     end
  314.     botLog("Final count of non-stackable items in inventory: " .. finalNonStackableItemsInTurtle)
  315.  
  316.     if finalNonStackableItemsInTurtle ~= expectedNonStackableCount then
  317.         -- Error case: Turn Right, then give warning & stop. critical_error_handler has 'doTurnRightForThisError'
  318.         critical_error_handler("Final non-stackable count (" .. finalNonStackableItemsInTurtle .. ") != initial (" .. expectedNonStackableCount .. ").", true);
  319.         return "error"
  320.     end
  321.     announce({{text="Final non-stackable item count ("..expectedNonStackableCount..") matches. Proceeding.", c=COLORS.GREEN}})
  322.     if check_stop_signal() then return "stopped_by_user" end
  323.  
  324.     -- Step 11: Deposit Upwards
  325.     botLog("Step 11: Turning right (to face North - Deposit Chest Above).")
  326.     turtle.turnRight()
  327.    
  328.     botLog("Dropping all items upwards (from slots 1-16).")
  329.     for i = 1, 16 do
  330.         turtle.select(i)
  331.         if turtle.getItemCount(i) > 0 then
  332.             if turtle.dropUp() then botLog("Dropped up items from slot " .. i)
  333.             else botLog("Failed to dropUp from slot " .. i .. " (chest above full or no item).") end
  334.         end
  335.     end
  336.     if check_stop_signal() then return "stopped_by_user" end
  337.  
  338.     botLog("== Disenchant Cycle Iteration Complete ==")
  339.     announce({{text="Disenchant cycle iteration finished. Starting next.",c=COLORS.GRAY}})
  340.     return "continue_looping" -- Signal to immediately start the next cycle
  341. end
  342.  
  343. commandHandlers.start = function(u,a)
  344.     if isDisenchantRunning then announce({{text=CHAT_BOT_NAME.." is already running.",c=COLORS.YELLOW}}); return end
  345.     isDisenchantRunning = true; fuelWarningSent = false
  346.     announce({{text="Starting "..CHAT_BOT_NAME.." loop...",c=COLORS.GOLD,b=true}})
  347.     botLog(u.." initiated "..CHAT_BOT_NAME.." start.")
  348. end
  349.  
  350. commandHandlers.stop = function(u,a)
  351.     if not isDisenchantRunning then announce({{text=CHAT_BOT_NAME.." is not currently running.",c=COLORS.GRAY}}); return end
  352.     announce({{text="Stop signal sent. Will stop after current major step/cycle.",c=COLORS.YELLOW}})
  353.     botLog(u.." initiated "..CHAT_BOT_NAME.." stop.")
  354.     isDisenchantRunning = false
  355. end
  356. --#endregion
  357.  
  358. --#region DisenchantBot Main Loop
  359. local function run()
  360.     if DEBUG_LOGGING_ENABLED then local f, err = fs.open(LOG_FILE_NAME, "w"); if f then f.write(string.format("[%s] %s Log Initialized.\n", os.date("%Y-%m-%d %H:%M:%S"), CHAT_BOT_NAME)); f.close() else print("LOG ERR: "..tostring(err)) end end
  361.     term.clear();term.setCursorPos(1,1);
  362.     automataPeripheral = peripheral.find(AUTOMATA_PERIPHERAL_NAME) -- Initial find for goHome()
  363.     if not automataPeripheral then botLog("WARN: EndAutomata ('"..AUTOMATA_PERIPHERAL_NAME.."') not found at startup. `goHome` will fail.") end
  364.  
  365.     chatBox = peripheral.find(CHAT_BOX_PERIPHERAL_NAME)
  366.     botLog(CHAT_BOT_NAME .. " initializing..."); botLog("Initial ChatBox peripheral check: " .. tostring(chatBox))
  367.     if chatBox and chatBox.sendFormattedMessage then chatBoxFunctional = true; botLog("ChatBox FUNCTIONAL.")
  368.     else chatBoxFunctional = false; botLog("ChatBox IS NIL or missing methods. Non-functional.") end
  369.    
  370.     botLog(CHAT_BOT_NAME.." online."); print(CHAT_BOT_NAME.." online. Use '"..COMMAND_PREFIX.." help'.")
  371.     if not announce({{text=CHAT_BOT_NAME.." online, ready for disenchanting!",c=COLORS.GREEN}}) then botLog("Initial online announcement FAILED.") end
  372.  
  373.     while true do
  374.         local event, p1, p2
  375.         if isDisenchantRunning then event, p1, p2 = os.pullEventRaw(); if not event then sleep(0.05) end -- Non-blocking when running cycle
  376.         else event, p1, p2 = os.pullEvent() end -- Blocking when idle
  377.  
  378.         if event then
  379.             if event == "chat" then
  380.                 local user, message = p1, p2
  381.                 if message then
  382.                     if string.lower(message) == "@all" then botLog("@all from "..user); announce({{text="'"..COMMAND_PREFIX.." help' for my cmds.",c=COLORS.GREEN}})
  383.                     elseif string.sub(message,1,#COMMAND_PREFIX) == COMMAND_PREFIX then
  384.                         botLog("Chat cmd from "..user..": "..message)
  385.                         local params={};for pt in string.gmatch(message,"[^%s]+")do table.insert(params,pt)end
  386.                         local cmdName=""; if params[2]then cmdName=string.lower(params[2])end
  387.                         local cmdArgs={};for i=3,#params do table.insert(cmdArgs,params[i])end
  388.                         if commandHandlers[cmdName]then commandHandlers[cmdName](user,cmdArgs)
  389.                         elseif cmdName ~= "" then announce({{text="Unknown Cmd: '",c=COLORS.RED},{text=cmdName,c=COLORS.YELLOW},{text="'. Try '",c=COLORS.RED},{text=COMMAND_PREFIX.." help",c=COLORS.AQUA},{text="'.",c=COLORS.RED}}) end
  390.                     end
  391.                 end
  392.             elseif event == "terminate" then botLog("Terminate event. Shutting down."); isDisenchantRunning=false; if chatBoxFunctional then announce({{text=CHAT_BOT_NAME.." shutting down.",c=COLORS.YELLOW}}) end; return end
  393.         end
  394.  
  395.         if isDisenchantRunning then -- Check if should run a cycle iteration
  396.             local cycleStatus = performSingleDisenchantCycle()
  397.             if cycleStatus == "error" or cycleStatus == "stopped_by_user" then
  398.                 botLog(CHAT_BOT_NAME .." loop explicitly stopped due to: " .. cycleStatus)
  399.                 isDisenchantRunning = false -- Ensure it's stopped
  400.             end
  401.             -- If "continue_looping", the outer while true and isDisenchantRunning handles the next iteration.
  402.             if not isDisenchantRunning then announce({{text=CHAT_BOT_NAME.." loop has stopped.",c=COLORS.GOLD}}); botLog(CHAT_BOT_NAME.." processing fully stopped.") end
  403.         end
  404.     end
  405. end
  406. run()
  407. --#endregion
Add Comment
Please, Sign In to add comment