This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Justinjah91's profile picture

Updating turtle programs

Started by Justinjah91, 10 October 2018 - 10:03 PM
Justinjah91 #1
Posted 11 October 2018 - 12:03 AM
Good afternoon!

So I keep all of my turtle programs on a single disk so I only have to edit the programs in one place. Any time I make changes to those programs, I update my turtles by running an update script that looks like this:


local programs = {"branch","dig","dump","loggerpro","move","obsidian","processgravel","room","safebranch","shaft","sort","sortwrapper"}

for i=1,#programs do
shell.run('delete',programs[i])
shell.run('copy','/disk/'..programs[i],programs[i])
end

print('All programs updated!')

As you can see, I manually enter the names of the programs into a table (or array, or matrix, or whatever they are called in LUA) to do this. I'm wondering if there is a way to automatically detect which programs a turtle already has installed and only attempt an update if the disk contains the same program. In other words, is there a way to avoid having to manually type in the names of all of the programs on the disk? It isn't a huge deal since there aren't very many programs here, I was just curious if it COULD be done.
Bomb Bloke #2
Posted 11 October 2018 - 01:07 AM
fs.list() gets you a table with all the contents of a given path. After pulling listings for both your turtle's drive and the floppy disk, you need only compare them and update any files found in both locations.
campicus #3
Posted 11 October 2018 - 02:01 AM
Not what you're looking for but your question has already been answered so… you might find this useful? Check out the program I made to automatically pull all the pastes from your pastebin. There's also a 'refresh' program I just added, which automatically updates all your programs when you place the comp/turtle next to the disk drive.
1. Run the update program on a computer above a disk drive (with a disk in it).
2. Place your turtle/computer next to the disk drive, open it
3. You'll be prompted to add a label (if it's not labelled) and set a startup program (leave blank to not set one)
4. Fully updated comp/turtle!

Update: https://pastebin.com/BB7dQGu2
Spoiler

local arg = { ... }
if #arg < 2 then
  -- Check to see if there are saved login details
  if fs.exists("credentials") then
	local h = fs.open("credentials", "r")
	local data = h.readAll()
	h.close()
	local data = textutils.unserialize(data)
	api_user_name = data[1]
	api_user_password = data[2]
  else
	term.clear()
	term.setCursorPos(1,1)
	error("Login credentials not specified. Usage: update <username> <password> [-s:save credentials locally]", 0)
  end
else
  api_user_name = arg[1]
  api_user_password = arg[2]
  if fs.exists("username") ~= true then
	local h = fs.open("username", "w")
	h.write(api_user_name)
	h.close()
  if arg[3] == "-s" then
	local h = fs.open("credentials", "w")
	h.write(textutils.serialize({api_user_name, api_user_password}))
	h.close()
  end
end

-- You need to put your api_dev_key here
local api_dev_key = "dfe49e216beea148e6acf8d5149d93f1" -- e.g., "cfe49e216beea143e6acf8h5149d93f1"

-- Authenticates with Pastebin.
-- Example: local agente382 = pastebin.login("AgentE382", "thisisnotmypassword")
--
-- Returns user key for use with other API functions.
function login(api_user_name, api_user_password)
	assert(type(api_user_name) == "string" and type(api_user_password) == "string", "Both arguments are required and must be strings!")
	return http.post(
		"https://pastebin.com/api/api_login.php",
		"api_dev_key="..api_dev_key.."&amp;"..
		"api_user_name="..textutils.urlEncode(api_user_name).."&amp;"..
		"api_user_password="..textutils.urlEncode(api_user_password)
		).readAll()
end

-- Internal function that parses the pastebin XML response.
local function parse(response)
	local objs = {}
	for prefix, objstr in string.gmatch(response, "<(.-)>(.-)</%1>") do
		local obj = {}
		for key, value in string.gmatch(objstr, "<"..prefix.."_(.-)>(.-)</"..prefix.."_%1>") do
			obj[key] = value
		end
		objs[#objs+1] = obj
	end
	return objs
end

-- Used to be a custom function, but I realized CC-Lua had one already.
local url_encode = textutils.urlEncode

-- Fetches a paste from Pastebin.
-- Example: local rc4 = pastebin.get("Rxe673BJ")
--
-- Returns requested paste as a string or an error message.
function get(api_paste_key)
	assert(type(api_paste_key) == "string", "Enter a valid paste key as a string!")
	local response = http.get("https://pastebin.com/raw.php?i="..url_encode(api_paste_key))
	return response and response.readAll()
end

-- Lists data about all of a user's pastes. Uses user key from login(). Note that this makes it impossible to get data about random people's pastes.
-- Example: local allpastes = pastebin.list(agente382, 1000)
--
-- Returns a list of data about all pastes associated with a user's account, using the table format described at the end of the document.
function list(api_user_key, api_results_limit)
	assert(type(api_user_key) == "string", "Enter a valid user key as a string!")
	local urlstr = {"api_dev_key=", url_encode(api_dev_key), "&amp;api_user_key=", url_encode(api_user_key), "&amp;api_option=list"}
	if api_results_limit then if type(api_results_limit) == "number" then if api_results_limit > 1000 then api_results_limit = 1000 elseif api_results_limit < 1 then api_results_limit = 1 end local len = #urlstr urlstr[len+1] = "&amp;api_results_limit=" urlstr[len+2] = url_encode(api_results_limit) else io.stderr:write("Results limit must be a number!\n") end end
	local response = http.post("https://pastebin.com/api/api_post.php", table.concat(urlstr)).readAll()
	if string.find(response,"<") then
		return parse(response)
	else
		return response
	end
end

-- Table Format:
--	  Pastes:
--	  {
--		  [1] = {
--			  date = "1297953260",
--			  format_short = "javascript",
--			  key = "0b42rwhf",
--			  size = "15",
--			  hits = "15",
--			  format_long = "JavaScript",
--			  url = "https://pastebin.com/0b42rwhf",
--			  title = "javascript test",
--			  private = "0",		-- 0:Public - 1:Unlisted - 2:Private
--			  expire_date = "1297956860"
--		  },
--		  [2] = {
--			  date = "1297694343",
--			  format_short = "text",
--			  key = "0C343n0d",
--			  size = "490",
--			  hits = "65",
--			  format_long = "None",
--			  url = "https://pastebin.com/0C343n0d",
--			  title = "Welcome To Pastebin V3",
--			  private = "0",		-- 0:Public - 1:Unlisted - 2:Private
--			  expire_date = "0"
--		  }
--	  }
------------------------

term.clear()
term.setCursorPos(1,1)
print("Preparing to update programs...")

user_data = login(api_user_name, api_user_password)
local allPastes = list(user_data) --"put all pastes in a table"
local folderName = api_user_name
if peripheral.isPresent("bottom") and peripheral.getType("bottom") == "drive" then
  print("Disk drive found, updating to disk.")
  sleep(1)
  if fs.exists("/disk/" ..folderName.. "/updates") then --"remove the old updates folder"
	fs.delete("/disk/" ..folderName.. "/updates")
  end
  if fs.exists("/disk/" ..folderName) then --"remove the old folder"
	fs.delete("/disk/" ..folderName)
  end
  fs.makeDir("disk/" ..folderName) --"make a new updates folder"
  downloadDir = "disk/" ..folderName.. "/"
else
  print("Updating computer.")
  sleep(1)
  if fs.exists("/" ..folderName.. "/updates") then --"remove the old updates folder"
	fs.delete("/" ..folderName.. "/updates")
  end
  fs.makeDir("/" ..folderName) --"make a new updates folder"
  downloadDir = folderName.. "/"
end

for i,v in ipairs(allPastes) do --"for each of my pastes download it and save it (with the name it has on pastebin)"
  code = http.get("https://pastebin.com/raw.php?i="..allPastes[i]["key"])
  if code then
		h = fs.open(downloadDir..allPastes[i]["title"],"w")
		h.write(code.readAll())
		h.close()
		term.setCursorPos(1,4)
		print("\""..allPastes[i]["title"].."\" successfully downloaded						 ")
  else
		print(allPastes[i]["title"] "failed to download")
  end
end

term.setCursorPos(1,4)
print("Successfully updated "..#allPastes.." programs.")
if downloadDir == "disk/" ..folderName then
  fs.delete("/" ..folderName)
  fs.copy("disk/" ..folderName, folderName)
  print("Successfully updated computer.")
end
if downloadDir == "disk/" ..folderName.. "/" then
  print("Setting disk startup program.")
  if fs.exists("/disk/startup") then
	fs.delete("/disk/startup")
  end
  fs.copy("disk/" ..folderName.. "/refresh", "disk/startup")
  print("Done.")
end

Refresh: https://pastebin.com/9mTVTruQ
Spoiler

local start = nil
local label = nil

if fs.exists("username") then
  local h = fs.open("username", "r")
  local api_user_name = h.readAll()
  h.close()
end

term.clear()
term.setCursorPos(1,1)

if os.computerLabel() == nil then
  print("What label do you want to give this device?")
  label = io.read()
  os.setComputerLabel(label)
else
label = os.computerLabel()
end

if fs.exists("startup") then
	fs.delete("/startup")
end

if os.computerLabel() == "compressor" then
  start = api_user_name.."/compressor"
elseif os.computerLabel() == "cComp" then
  start = ""
  shell.run("disk/"..api_user_name.."/update")
else
  print("What program do you want to set as startup?")
  start = io.read()
end
if start ~= "" then
  h = fs.open("startup", "w")
  h.write("shell.run\(\""..start.."\"\)")
  h.close()
end

if fs.exists(api_user_name) then
  fs.delete("/"..api_user_name)
end
fs.copy("/disk/"..api_user_name,"/"..api_user_name)

print("Device '"..label.."' has been updated")
if start ~= "" then
	print("Startup program set to "..start)
else
	print("No startup program set")
end
Edited on 11 October 2018 - 12:02 AM