View difference between Paste ID: 86Kjhq32 and hbtxYxz5
SHOW: | | - or go back to the newest paste.
1-
local ec = peripheral.find "ender_chest"
1+
package.path = "/?;/?.lua;" .. package.path
2-
local ecinv = peripheral.find "minecraft:ender chest"
2+
local chest = settings.get "mail.chest" or error "please set mail.chest to the network name of the (non-ender) chest to use"
3
local ender_chest = peripheral.find "ender_chest" or error "ender chest connected through adapter + relay required"
4-
local f = fs.open("escan.log", "w")
4+
local ender_chest_inv = peripheral.find "minecraft:ender chest" or error "ender chest directly connected required"
5
local modem = peripheral.find("modem", function(_, x) return x.isWireless() end) or error "wireless modem required"
6-
local z = ...
6+
local ok, ecnet = pcall(require, "ecnet")
7-
if z then
7+
if not ok then
8-
    ec.setFrequency(tonumber(z, 16))
8+
	print "Downloading ECNet library (https://forums.computercraft.cc/index.php?topic=181.0)"
9-
    return
9+
	shell.run "wget https://gist.githubusercontent.com/migeyel/278f77628248ea991719f0376979b525/raw/ecnet.min.lua ecnet.lua"
10
end
11
ecnet = require "ecnet"
12-
for i = 0, 0xFFF do
12+
local label = os.getComputerLabel() or error "Please set a label to use as a device name"
13-
    ec.setFrequency(i)
13+
print("Address is", ecnet.address)
14-
    local count = 0
14+
local ecnet_modem = ecnet.wrap(modem)
15-
    for _, s in pairs(ecinv.list()) do
15+
local maildata_path = "maildata"
16-
        count = count + s.count
16+
17
local acceptable_mailbox_name_pattern = "^[A-Za-z0-9_]+$"
18-
    if count > 0 then
18+
if not label:match(acceptable_mailbox_name_pattern) then error("label must match: " .. acceptable_mailbox_name_pattern) end
19-
		local log = ("%s %s 0x%03x %d"):format(os.date "!%X", table.concat(ec.getFrequencyColors(), "/"), i, count)
19+
20-
        print(log)
20+
local function find_channel()
21-
		f.writeLine(log)
21+
	for i = 0, 10 do
22
		local new = math.random(0, 0xFFF)
23-
	if i % 256 == 255 then
23+
		ender_chest.setFrequency(new)
24-
		f.flush()
24+
		local count = 0
25
		for _, stack in pairs(ender_chest_inv.list()) do
26-
    os.queueEvent ""
26+
			count = count + stack.count
27-
	os.pullEvent ""
27+
		end
28
		if count == 0 then
29
			return new
30-
f.close()
30+
		end
31
	end
32
	error "Available channel scan failed after 10 tries - has someone flooded ender chests with random stuff?"
33
end
34
35
local function writef(n, c)
36
	local f = fs.open(n, "w")
37
	f.write(c)
38
	f.close()
39
end
40
41
local function readf(n)
42
	local f = fs.open(n, "r")
43
	local out = f.readAll()
44
	f.close()
45
	return out
46
end
47
48
local data = {}
49
if fs.exists(maildata_path) then data = textutils.unserialise(readf(maildata_path)) end
50
if type(data.paired) ~= "table" then data.paired = {} end
51
52
local function save_data() writef(maildata_path, textutils.serialise(data)) end
53
54
local function split_at_spaces(s)
55
    local t = {}
56
    for i in string.gmatch(s, "%S+") do
57
       table.insert(t, i)
58
    end
59
    return t
60
end
61
62
local function update_self()
63
	print "Downloading update."
64
	local h = http.get "https://pastebin.com/raw/86Kjhq32"
65
	local t = h.readAll()
66
	h.close()
67
	local fn, err = load(t, "@mail")
68
	if not fn then printError("Not updating: syntax error in new version:\n" .. err) return end
69
	local f = fs.open("startup", "w")
70
	f.write(t)
71
	f.close()
72
	os.reboot()
73
end
74
75
local function first_letter(s)
76
    return string.sub(s, 1, 1)
77
end
78
79
local function send_stack(slot, addr)
80
	local channel = find_channel()
81
	print("[OUT] Channel:", channel)
82
	ecnet_modem.send(addr, { "stack_request", channel = channel })
83
	local _, result = os.pullEvent "stack_request_response"
84
	if result == true then
85
		ender_chest_inv.pullItems(chest, slot)
86
		print("[OUT] Sent stack", slot)
87
		local _, result, x = os.pullEvent "stack_result"
88
		if result == false then
89
			printError("[OUT] Destination error: " .. tostring(x))
90
			for eslot in pairs(ender_chest_inv.list()) do
91
				ender_chest_inv.pushItems(chest, eslot)
92
			end
93
		end
94
		return result
95
	else return false end
96
end
97
98
local function get_name(address)
99
	for name, addr in pairs(data.paired) do
100
		if addr == address then return name end
101
	end
102
	return address
103
end
104
105
local last_pair_request = nil
106
107
local CLI_commands = {
108
	address = function()
109
		print(ecnet.address)
110
	end,
111
	update = update_self,
112
	pair = function(addr)
113
		local ok = ecnet_modem.connect(addr, 2)
114
		if not ok then error("Could not contact " .. addr) end
115
		ecnet_modem.send(addr, { "pair", label = label })
116
	end,
117
	accept_pair = function()
118
		if not last_pair_request then error "no pair request to accept" end
119
		ecnet_modem.send(last_pair_request.address, { "pair_accept", label = label })
120
		data.paired[last_pair_request.label] = last_pair_request.address
121
		save_data()
122
		last_pair_request = nil
123
	end,
124
	reject_pair = function()
125
		if not last_pair_request then error "no pair request to reject" end
126
		ecnet_modem.send(last_pair_request.address, { "pair_reject", label = label })
127
		last_pair_request = nil
128
	end,
129
	paired = function()
130
		print "Paired:"
131
		for label, addr in pairs(data.paired) do
132
			print(label, addr)
133
		end
134
	end,
135
	unpair = function(name)
136
		data.paired[name] = nil
137
		save_data()
138
	end,
139
	send = function(name)
140
		local addr = data.paired[name]
141
		if not addr then error(name .. " not found") end
142
		if not ecnet_modem.connect(addr, 3) then error("Connection to " .. name .. " failed") end
143
		print "Connected"
144
		for slot, contents in pairs(peripheral.call(chest, "list")) do
145
			print("[OUT] Sending stack", slot)
146
			local timed_out, result = false, nil
147
			parallel.waitForAny(function() result = send_stack(slot, addr) end, function() sleep(5) timed_out = true end)
148
			if not timed_out then print("[OUT] Destination success") else printError "[OUT] Timed out." end
149
		end
150
	end,
151
	help = function()
152
		write([[EnderMail UI commands:
153
address - print address
154
update - update the code
155
pair [address] - send a pairing request to the specified address
156
accept_pair - accept the latest pairing request
157
deny_pair - reject the latest pairing request
158
paired - list all paired mailboxes
159
unpair [name] - remove the named mailbox from your paired list
160
send [name] - send contents of chest to specified paired mailbox
161
]])
162
	end
163
}
164
165
local function handle_commands()
166
	print "Mailbox UI"
167
	local history = {}
168
	while true do
169
		write "|> "
170
		local text = read(nil, history)
171
172
		if text ~= "" then table.insert(history, text) end
173
174
		local tokens = split_at_spaces(text)
175
		local command = table.remove(tokens, 1)
176
		local args = tokens
177
		local fn = CLI_commands[command]
178
179
		if not fn then
180
			for command_name, func in pairs(CLI_commands) do
181
				if command and first_letter(command_name) == first_letter(command) then fn = func end
182
			end
183
		end
184
		if not fn then
185
			print("Command", command, "not found.")
186
		else
187
			local ok, err = pcall(fn, table.unpack(args))
188
			if not ok then printError(err) end
189
		end
190
	end
191
end
192
193
local function handle_message(addr, msg)
194
	if type(msg) == "table" then
195
		if msg[1] == "pair" then
196
			if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
197
			print(("Pair request from %s (%s)"):format(addr, msg.label))
198
			print "`accept_pair` to accept, `reject_pair` to deny"
199
			last_pair_request = { address = addr, label = msg.label }
200
		elseif msg[1] == "pair_accept" then
201
			if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
202
			print(("%s (%s) accepted pairing"):format(addr, msg.label))
203
			data.paired[msg.label] = addr
204
			save_data()
205
		elseif msg[1] == "pair_reject" then
206
			if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
207
			print(("%s (%s) rejected pairing"):format(addr, msg.label))
208
		elseif msg[1] == "stack_request" then
209
			if not msg.channel or msg.channel < 0 or msg.channel > 0xFFF then ecnet_modem.send(addr, { "stack_request_response", false, "channel missing/invalid" }) end
210
			ender_chest.setFrequency(msg.channel)
211
			ecnet_modem.send(addr, { "stack_request_response", true })
212
			local start = os.clock()
213
			-- constantly attempt to move items until done
214
			while os.clock() - start <= 5 do
215
				for slot, stack in pairs(ender_chest_inv.list()) do
216
					local moved = ender_chest_inv.pushItems(chest, slot)
217
					print("[IN]", get_name(addr), stack.name, moved)
218
					if moved > 0 then
219
						ecnet_modem.send(addr, { "stack_result", true, channel = msg.channel })
220
						return
221
					else
222
						ecnet_modem.send(addr, { "stack_result", false, "out of space", channel = msg.channel })
223
						return
224
					end
225
				end
226
			end
227
			ecnet_modem.send(addr, { "stack_result", false, channel = msg.channel })
228
		elseif msg[1] == "stack_request_response" then
229
			os.queueEvent("stack_request_response", msg[2])
230
		elseif msg[1] == "stack_result" then
231
			os.queueEvent("stack_result", msg[2], msg[3])
232
		end
233
	end
234
end
235
236
local function handle_messages()
237
	while true do
238
		handle_message(ecnet_modem.receive())
239
	end
240
end
241
242
parallel.waitForAll(handle_commands, handle_messages)