spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access

This commit is contained in:
kepler155c@gmail.com
2019-06-18 15:23:20 -04:00
parent 3b9b509429
commit 045b32884f
162 changed files with 20448 additions and 20286 deletions

View File

@@ -8,7 +8,7 @@ local CONFIG_FILE = '/usr/config/milo.state'
local config = Util.readTable(CONFIG_FILE) or { }
if not config.plugins then
config.plugins = { }
config.plugins = { }
end
local dir = fs.getDir(shell.getRunningProgram())

View File

@@ -5,118 +5,118 @@ eventRegistry = {}
timedRegistry = {}
local function registerEvent(event, callback)
if eventRegistry[event] == nil then
eventRegistry[event] = {}
end
if eventRegistry[event] == nil then
eventRegistry[event] = {}
end
table.insert(eventRegistry[event], callback)
table.insert(eventRegistry[event], callback)
end
local function registerTimed(time, repeating, callback)
if repeating then
callback(true)
end
if repeating then
callback(true)
end
table.insert(timedRegistry, {
time = time,
repeating = repeating,
callback = callback,
timer = os.startTimer(time)
})
table.insert(timedRegistry, {
time = time,
repeating = repeating,
callback = callback,
timer = os.startTimer(time)
})
end
local function discoverEvents(event)
local evs = {}
for k,v in pairs(eventRegistry) do
if k == event or string.match(k, event) or event == "*" then
for i,v2 in ipairs(v) do
table.insert(evs, v2)
end
end
end
local evs = {}
for k,v in pairs(eventRegistry) do
if k == event or string.match(k, event) or event == "*" then
for i,v2 in ipairs(v) do
table.insert(evs, v2)
end
end
end
return evs
return evs
end
function on(event, callback)
registerEvent(event, callback)
registerEvent(event, callback)
end
function setInterval(callback, time)
registerTimed(time, true, callback)
registerTimed(time, true, callback)
end
function setTimeout(callback, time)
registerTimed(time, false, callback)
registerTimed(time, false, callback)
end
function tick()
local eargs = {os.pullEventRaw()}
local event = eargs[1]
local eargs = {os.pullEventRaw()}
local event = eargs[1]
if eventRegistry[event] == nil then
eventRegistry[event] = {}
else
local evs = discoverEvents(event)
for i, v in ipairs(evs) do
v(unpack(eargs))
end
end
if eventRegistry[event] == nil then
eventRegistry[event] = {}
else
local evs = discoverEvents(event)
for i, v in ipairs(evs) do
v(unpack(eargs))
end
end
if event == "timer" then
local timer = eargs[2]
if event == "timer" then
local timer = eargs[2]
for i = #timedRegistry, 1, -1 do
local v = timedRegistry[i]
if v.timer == timer then
v.callback(not v.repeating or nil)
for i = #timedRegistry, 1, -1 do
local v = timedRegistry[i]
if v.timer == timer then
v.callback(not v.repeating or nil)
if v.repeating then
v.timer = os.startTimer(v.time)
else
table.remove(timedRegistry, i)
end
end
end
end
if v.repeating then
v.timer = os.startTimer(v.time)
else
table.remove(timedRegistry, i)
end
end
end
end
end
function run()
os.queueEvent("init")
juaRunning = true
while juaRunning do
tick()
end
os.queueEvent("init")
juaRunning = true
while juaRunning do
tick()
end
end
function go(func)
on("init", func)
run()
on("init", func)
run()
end
function stop()
juaRunning = false
juaRunning = false
end
function await(func, ...)
local args = {...}
local out
local finished
func(function(...)
out = {...}
finished = true
end, unpack(args))
while not finished do tick() end
return unpack(out)
local args = {...}
local out
local finished
func(function(...)
out = {...}
finished = true
end, unpack(args))
while not finished do tick() end
return unpack(out)
end
return {
on = on,
setInterval = setInterval,
setTimeout = setTimeout,
tick = tick,
run = run,
go = go,
stop = stop,
await = await
on = on,
setInterval = setInterval,
setTimeout = setTimeout,
tick = tick,
run = run,
go = go,
stop = stop,
await = await
}

View File

@@ -9,163 +9,163 @@ local wsEndpoint = "ws://"..endpoint
local httpEndpoint = "http://"..endpoint
local function asserttype(var, name, vartype, optional)
if not (type(var) == vartype or optional and type(var) == "nil") then
error(name..": expected "..vartype.." got "..type(var), 3)
end
if not (type(var) == vartype or optional and type(var) == "nil") then
error(name..": expected "..vartype.." got "..type(var), 3)
end
end
function init(juai, jsoni, wi, ri)
asserttype(juai, "jua", "table")
asserttype(jsoni, "json", "table")
asserttype(wi, "w", "table", true)
asserttype(ri, "r", "table")
asserttype(juai, "jua", "table")
asserttype(jsoni, "json", "table")
asserttype(wi, "w", "table", true)
asserttype(ri, "r", "table")
jua = juai
await = juai.await
json = jsoni
w = wi
r = ri
jua = juai
await = juai.await
json = jsoni
w = wi
r = ri
end
local function prints(...)
local objs = {...}
for i, obj in ipairs(objs) do
print(textutils.serialize(obj))
end
local objs = {...}
for i, obj in ipairs(objs) do
print(textutils.serialize(obj))
end
end
local function url(call)
return httpEndpoint..call
return httpEndpoint..call
end
local function api_request(cb, api, data)
local success, url, handle = await(r.request, url(api) .. (api:find("%%?") and "?cc" or "&cc"), {["Content-Type"]="application/json"}, data and json.encode(data))
if success then
cb(success, json.decode(handle.readAll()))
handle.close()
else
cb(success)
end
local success, url, handle = await(r.request, url(api) .. (api:find("%%?") and "?cc" or "&cc"), {["Content-Type"]="application/json"}, data and json.encode(data))
if success then
cb(success, json.decode(handle.readAll()))
handle.close()
else
cb(success)
end
end
local function authorize_websocket(cb, privatekey)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string", true)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string", true)
api_request(function(success, data)
cb(success and data and data.ok, data.url and data.url:gsub("wss:", "ws:") or data)
end, "/ws/start", {
privatekey = privatekey
})
api_request(function(success, data)
cb(success and data and data.ok, data.url and data.url:gsub("wss:", "ws:") or data)
end, "/ws/start", {
privatekey = privatekey
})
end
function address(cb, address)
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
api_request(function(success, data)
if data.address then
data.address.address = address
end
cb(success and data and data.ok, data.address or data)
end, "/addresses/"..address)
api_request(function(success, data)
if data.address then
data.address.address = address
end
cb(success and data and data.ok, data.address or data)
end, "/addresses/"..address)
end
function addressTransactions(cb, address, limit, offset)
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/addresses/"..address.."/transactions?limit="..(limit or 50).."&offset="..(offset or 0))
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/addresses/"..address.."/transactions?limit="..(limit or 50).."&offset="..(offset or 0))
end
function addressNames(cb, address)
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
asserttype(cb, "callback", "function")
asserttype(address, "address", "string")
api_request(function(success, data)
cb(success and data and data.ok, data.names or data)
end, "/addresses/"..address.."/names")
api_request(function(success, data)
cb(success and data and data.ok, data.names or data)
end, "/addresses/"..address.."/names")
end
function addresses(cb, limit, offset)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
api_request(function(success, data)
cb(success and data and data.ok, data.addresses or data)
end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0))
api_request(function(success, data)
cb(success and data and data.ok, data.addresses or data)
end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0))
end
function rich(cb, limit, offset)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
api_request(function(success, data)
cb(success and data and data.ok, data.addresses or data)
end, "/addresses/rich?limit="..(limit or 50).."&offset="..(offset or 0))
api_request(function(success, data)
cb(success and data and data.ok, data.addresses or data)
end, "/addresses/rich?limit="..(limit or 50).."&offset="..(offset or 0))
end
function transactions(cb, limit, offset)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/transactions?limit="..(limit or 50).."&offset="..(offset or 0))
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/transactions?limit="..(limit or 50).."&offset="..(offset or 0))
end
function latestTransactions(cb, limit, offset)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
asserttype(cb, "callback", "function")
asserttype(limit, "limit", "number", true)
asserttype(offset, "offset", "number", true)
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/transactions/latest?limit="..(limit or 50).."&offset="..(offset or 0))
api_request(function(success, data)
cb(success and data and data.ok, data.transactions or data)
end, "/transactions/latest?limit="..(limit or 50).."&offset="..(offset or 0))
end
function transaction(cb, txid)
asserttype(cb, "callback", "function")
asserttype(txid, "txid", "number")
asserttype(cb, "callback", "function")
asserttype(txid, "txid", "number")
api_request(function(success, data)
cb(success and data and data.ok, data.transaction or data)
end, "/transactions/"..txid)
api_request(function(success, data)
cb(success and data and data.ok, data.transaction or data)
end, "/transactions/"..txid)
end
function makeTransaction(cb, privatekey, to, amount, metadata)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string")
asserttype(to, "to", "string")
asserttype(amount, "amount", "number")
asserttype(metadata, "metadata", "string", true)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string")
asserttype(to, "to", "string")
asserttype(amount, "amount", "number")
asserttype(metadata, "metadata", "string", true)
api_request(function(success, data)
cb(success and data and data.ok, data.transaction or data)
end, "/transactions", {
privatekey = privatekey,
to = to,
amount = amount,
metadata = metadata
})
api_request(function(success, data)
cb(success and data and data.ok, data.transaction or data)
end, "/transactions", {
privatekey = privatekey,
to = to,
amount = amount,
metadata = metadata
})
end
local wsEventNameLookup = {
blocks = "block",
ownBlocks = "block",
transactions = "transaction",
ownTransactions = "transaction",
names = "name",
ownNames = "name",
ownWebhooks = "webhook",
motd = "motd"
blocks = "block",
ownBlocks = "block",
transactions = "transaction",
ownTransactions = "transaction",
names = "name",
ownNames = "name",
ownWebhooks = "webhook",
motd = "motd"
}
local wsEvents = {}
@@ -176,238 +176,238 @@ local wsEvtRegistry = {}
local wsHandleRegistry = {}
local function newWsID()
local id = wsReqID
wsReqID = wsReqID + 1
return id
local id = wsReqID
wsReqID = wsReqID + 1
return id
end
local function registerEvent(id, event, callback)
if wsEvtRegistry[id] == nil then
wsEvtRegistry[id] = {}
end
if wsEvtRegistry[id] == nil then
wsEvtRegistry[id] = {}
end
if wsEvtRegistry[id][event] == nil then
wsEvtRegistry[id][event] = {}
end
if wsEvtRegistry[id][event] == nil then
wsEvtRegistry[id][event] = {}
end
table.insert(wsEvtRegistry[id][event], callback)
table.insert(wsEvtRegistry[id][event], callback)
end
local function registerRequest(id, reqid, callback)
if wsReqRegistry[id] == nil then
wsReqRegistry[id] = {}
end
if wsReqRegistry[id] == nil then
wsReqRegistry[id] = {}
end
wsReqRegistry[id][reqid] = callback
wsReqRegistry[id][reqid] = callback
end
local function discoverEvents(id, event)
local evs = {}
for k,v in pairs(wsEvtRegistry[id]) do
if k == event or string.match(k, event) or event == "*" then
for i,v2 in ipairs(v) do
table.insert(evs, v2)
end
end
end
local evs = {}
for k,v in pairs(wsEvtRegistry[id]) do
if k == event or string.match(k, event) or event == "*" then
for i,v2 in ipairs(v) do
table.insert(evs, v2)
end
end
end
return evs
return evs
end
wsEvents.success = function(id, handle)
-- fire success event
wsHandleRegistry[id] = handle
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "success")
for i, v in ipairs(evs) do
v(id, handle)
end
end
-- fire success event
wsHandleRegistry[id] = handle
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "success")
for i, v in ipairs(evs) do
v(id, handle)
end
end
end
wsEvents.failure = function(id)
-- fire failure event
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "failure")
for i, v in ipairs(evs) do
v(id)
end
end
-- fire failure event
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "failure")
for i, v in ipairs(evs) do
v(id)
end
end
end
wsEvents.message = function(id, data)
local data = json.decode(data)
--print("msg:"..tostring(data.ok)..":"..tostring(data.type)..":"..tostring(data.id))
--prints(data)
-- handle events and responses
if wsReqRegistry[id] and wsReqRegistry[id][tonumber(data.id)] then
wsReqRegistry[id][tonumber(data.id)](data)
elseif wsEvtRegistry[id] then
local evs = discoverEvents(id, data.type)
for i, v in ipairs(evs) do
v(data)
end
local data = json.decode(data)
--print("msg:"..tostring(data.ok)..":"..tostring(data.type)..":"..tostring(data.id))
--prints(data)
-- handle events and responses
if wsReqRegistry[id] and wsReqRegistry[id][tonumber(data.id)] then
wsReqRegistry[id][tonumber(data.id)](data)
elseif wsEvtRegistry[id] then
local evs = discoverEvents(id, data.type)
for i, v in ipairs(evs) do
v(data)
end
if data.event then
local evs = discoverEvents(id, data.event)
for i, v in ipairs(evs) do
v(data)
end
end
if data.event then
local evs = discoverEvents(id, data.event)
for i, v in ipairs(evs) do
v(data)
end
end
local evs2 = discoverEvents(id, "message")
for i, v in ipairs(evs2) do
v(id, data)
end
end
local evs2 = discoverEvents(id, "message")
for i, v in ipairs(evs2) do
v(id, data)
end
end
end
wsEvents.closed = function(id)
-- fire closed event
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "closed")
for i, v in ipairs(evs) do
v(id)
end
end
-- fire closed event
if wsEvtRegistry[id] then
local evs = discoverEvents(id, "closed")
for i, v in ipairs(evs) do
v(id)
end
end
end
local function wsRequest(cb, id, type, data)
local reqID = newWsID()
registerRequest(id, reqID, function(data)
cb(data)
end)
data.id = tostring(reqID)
data.type = type
wsHandleRegistry[id].send(json.encode(data))
local reqID = newWsID()
registerRequest(id, reqID, function(data)
cb(data)
end)
data.id = tostring(reqID)
data.type = type
wsHandleRegistry[id].send(json.encode(data))
end
local function barebonesMixinHandle(id, handle)
handle.on = function(event, cb)
registerEvent(id, event, cb)
end
handle.on = function(event, cb)
registerEvent(id, event, cb)
end
return handle
return handle
end
local function mixinHandle(id, handle)
handle.subscribe = function(cb, event, eventcb)
local data = await(wsRequest, id, "subscribe", {
event = event
})
registerEvent(id, wsEventNameLookup[event], eventcb)
cb(data.ok, data)
end
handle.subscribe = function(cb, event, eventcb)
local data = await(wsRequest, id, "subscribe", {
event = event
})
registerEvent(id, wsEventNameLookup[event], eventcb)
cb(data.ok, data)
end
return barebonesMixinHandle(id, handle)
return barebonesMixinHandle(id, handle)
end
function connect(cb, privatekey, preconnect)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string", true)
asserttype(preconnect, "preconnect", "function", true)
local url
if privatekey then
local success, auth = await(authorize_websocket, privatekey)
url = success and auth or wsEndpoint
end
local id = w.open(wsEvents, url)
if preconnect then
preconnect(id, barebonesMixinHandle(id, {}))
end
registerEvent(id, "success", function(id, handle)
cb(true, mixinHandle(id, handle))
end)
registerEvent(id, "failure", function(id)
cb(false)
end)
asserttype(cb, "callback", "function")
asserttype(privatekey, "privatekey", "string", true)
asserttype(preconnect, "preconnect", "function", true)
local url
if privatekey then
local success, auth = await(authorize_websocket, privatekey)
url = success and auth or wsEndpoint
end
local id = w.open(wsEvents, url)
if preconnect then
preconnect(id, barebonesMixinHandle(id, {}))
end
registerEvent(id, "success", function(id, handle)
cb(true, mixinHandle(id, handle))
end)
registerEvent(id, "failure", function(id)
cb(false)
end)
end
local domainMatch = "^([%l%d-_]*)@?([%l%d-]+).kst$"
local commonMetaMatch = "^(.+)=(.+)$"
function parseMeta(meta)
asserttype(meta, "meta", "string")
local tbl = {meta={}}
asserttype(meta, "meta", "string")
local tbl = {meta={}}
for m in meta:gmatch("[^;]+") do
if m:match(domainMatch) then
-- print("Matched domain")
for m in meta:gmatch("[^;]+") do
if m:match(domainMatch) then
-- print("Matched domain")
local p1, p2 = m:match("([%l%d-_]*)@"), m:match("@?([%l%d-]+).kst")
tbl.name = p1
tbl.domain = p2
local p1, p2 = m:match("([%l%d-_]*)@"), m:match("@?([%l%d-]+).kst")
tbl.name = p1
tbl.domain = p2
elseif m:match(commonMetaMatch) then
-- print("Matched common meta")
elseif m:match(commonMetaMatch) then
-- print("Matched common meta")
local p1, p2 = m:match(commonMetaMatch)
local p1, p2 = m:match(commonMetaMatch)
tbl.meta[p1] = p2
tbl.meta[p1] = p2
else
-- print("Unmatched standard meta")
else
-- print("Unmatched standard meta")
table.insert(tbl.meta, m)
end
-- print(m)
end
-- print(textutils.serialize(tbl))
return tbl
table.insert(tbl.meta, m)
end
-- print(m)
end
-- print(textutils.serialize(tbl))
return tbl
end
local g = string.gsub
sha256 = loadstring(g(g(g(g(g(g(g(g('Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1]<q then r[2]=r[2]+1;r[1]=q-(e-1-r[1])-1 else r[1]=r[1]+qVUrVSYs(t)Su=#t;t[#t+1]=0x80;while#t%64~=56Zt[#t+1]=0VSv=q({0,0},u*8)fWw=2,1,-1Zt[#t+1]=a(k(a(v[w]TFF000000),24)TFF)t[#t+1]=a(k(a(v[w]TFF0000),16)TFF)t[#t+1]=a(k(a(v[w]TFF00),8)TFF)t[#t+1]=a(v[w]TFF)VUtVSYx(y,w)Uc(y[w]W0,24)+c(y[w+1]W0,16)+c(y[w+2]W0,8)+(y[w+3]W0)VSYz(t,w,A)SB={}fWC=1,16ZB[C]=x(t,w+(C-1)*4)VfWC=17,64ZSD=B[C-15]SE=b(b(f(B[C-15],7),f(B[C-15],18)),k(B[C-15],3))SF=b(b(f(B[C-2],17),f(B[C-2],19)),k(B[C-2],10))B[C]=(B[C-16]+E+B[C-7]+F)%eVSG,h,H,I,J,j,K,L=d(A)fWC=1,64ZSM=b(b(f(J,6),f(J,11)),f(J,25))SN=b(a(J,j),a(Xbnot(J),K))SO=(L+M+N+p[C]+B[C])%eSP=b(b(f(G,2),f(G,13)),f(G,22))SQ=b(b(a(G,h),a(G,H)),a(h,H))SR=(P+Q)%e;L,K,j,J,I,H,h,G=K,j,J,(I+O)%e,H,h,G,(O+R)%eVA[1]=(A[1]+G)%e;A[2]=(A[2]+h)%e;A[3]=(A[3]+H)%e;A[4]=(A[4]+I)%e;A[5]=(A[5]+J)%e;A[6]=(A[6]+j)%e;A[7]=(A[7]+K)%e;A[8]=(A[8]+L)%eUAVUY(t)t=t W""t=type(t)=="string"and{t:byte(1,-1)}Wt;t=s(t)SA={d(o)}fWw=1,#t,64ZA=z(t,w,A)VU("%08x"):rep(8):format(d(A))V',"S"," local "),"T",",0x"),"U"," return "),"V"," end "),"W","or "),"X","bit32."),"Y","function "),"Z"," do "))()
function makeaddressbyte(byte)
local byte = 48 + math.floor(byte / 7)
return string.char(byte + 39 > 122 and 101 or byte > 57 and byte + 39 or byte)
local byte = 48 + math.floor(byte / 7)
return string.char(byte + 39 > 122 and 101 or byte > 57 and byte + 39 or byte)
end
function makev2address(key)
local protein = {}
local stick = sha256(sha256(key))
local n = 0
local link = 0
local v2 = "k"
repeat
if n < 9 then protein[n] = string.sub(stick,0,2)
stick = sha256(sha256(stick)) end
n = n + 1
until n == 9
n = 0
repeat
link = tonumber(string.sub(stick,1+(2*n),2+(2*n)),16) % 9
if string.len(protein[link]) ~= 0 then
v2 = v2 .. makeaddressbyte(tonumber(protein[link],16))
protein[link] = ''
n = n + 1
else
stick = sha256(stick)
end
until n == 9
return v2
local protein = {}
local stick = sha256(sha256(key))
local n = 0
local link = 0
local v2 = "k"
repeat
if n < 9 then protein[n] = string.sub(stick,0,2)
stick = sha256(sha256(stick)) end
n = n + 1
until n == 9
n = 0
repeat
link = tonumber(string.sub(stick,1+(2*n),2+(2*n)),16) % 9
if string.len(protein[link]) ~= 0 then
v2 = v2 .. makeaddressbyte(tonumber(protein[link],16))
protein[link] = ''
n = n + 1
else
stick = sha256(stick)
end
until n == 9
return v2
end
function toKristWalletFormat(passphrase)
return sha256("KRISTWALLET"..passphrase).."-000"
return sha256("KRISTWALLET"..passphrase).."-000"
end
return {
init = init,
address = address,
addressTransactions = addressTransactions,
addressNames = addressNames,
addresses = addresses,
rich = rich,
transactions = transactions,
latestTransactions = latestTransactions,
transaction = transaction,
makeTransaction = makeTransaction,
connect = connect,
parseMeta = parseMeta,
sha256 = sha256,
makeaddressbyte = makeaddressbyte,
makev2address = makev2address,
toKristWalletFormat = toKristWalletFormat
init = init,
address = address,
addressTransactions = addressTransactions,
addressNames = addressNames,
addresses = addresses,
rich = rich,
transactions = transactions,
latestTransactions = latestTransactions,
transaction = transaction,
makeTransaction = makeTransaction,
connect = connect,
parseMeta = parseMeta,
sha256 = sha256,
makeaddressbyte = makeaddressbyte,
makev2address = makev2address,
toKristWalletFormat = toKristWalletFormat
}

View File

@@ -4,62 +4,62 @@ local idPatt = "#R%d+"
callbackRegistry = {}
local function gfind(str, patt)
local t = {}
for found in str:gmatch(patt) do
table.insert(t, found)
end
local t = {}
for found in str:gmatch(patt) do
table.insert(t, found)
end
if #t > 0 then
return t
else
return nil
end
if #t > 0 then
return t
else
return nil
end
end
local function findID(url)
local found = gfind(url, idPatt)
if found then
return tonumber(found[#found]:sub(found[#found]:find("%d+")))
end
local found = gfind(url, idPatt)
if found then
return tonumber(found[#found]:sub(found[#found]:find("%d+")))
end
end
local function newID()
return #callbackRegistry + 1
return #callbackRegistry + 1
end
local function trimID(url)
local found = gfind(url, idPatt)
local s, e = url:find(found[#found])
return url:sub(1, s-1)
local found = gfind(url, idPatt)
local s, e = url:find(found[#found])
return url:sub(1, s-1)
end
function request(callback, url, headers, postData)
local id = newID()
local newUrl = url .. "#R" .. id
http.request(newUrl, postData, headers)
callbackRegistry[id] = callback
local id = newID()
local newUrl = url .. "#R" .. id
http.request(newUrl, postData, headers)
callbackRegistry[id] = callback
end
function init(jua)
jua = jua
jua.on("http_success", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] then
callbackRegistry[id](true, trimID(url), handle)
table.remove(callbackRegistry, id)
end
end)
jua = jua
jua.on("http_success", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] then
callbackRegistry[id](true, trimID(url), handle)
table.remove(callbackRegistry, id)
end
end)
jua.on("http_failure", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] then
callbackRegistry[id](false, trimID(url), handle)
table.remove(callbackRegistry, id)
end
end)
jua.on("http_failure", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] then
callbackRegistry[id](false, trimID(url), handle)
table.remove(callbackRegistry, id)
end
end)
end
return {
request = request,
init = init
request = request,
init = init
}

View File

@@ -6,75 +6,75 @@ local os = _G.os
--[[ Configuration Page ]]--
local wizardPage = UI.WizardPage {
title = 'Store Front',
index = 2,
backgroundColor = colors.cyan,
form = UI.Form {
x = 2, ex = -2, y = 1, ey = -2,
manualControls = true,
title = 'Store Front',
index = 2,
backgroundColor = colors.cyan,
form = UI.Form {
x = 2, ex = -2, y = 1, ey = -2,
manualControls = true,
[1] = UI.TextEntry {
formLabel = 'Domain', formKey = 'domain',
help = 'Krist wallet domain (minus .kst)',
limit = 64,
shadowText = 'example',
required = true,
shadowText = 'example',
required = true,
},
[2] = UI.TextEntry {
formLabel = 'Password', formKey = 'password',
shadowText = 'password',
limit = 256,
required = true,
required = true,
help = 'Krist wallet password',
},
[3] = UI.TextEntry {
formLabel = 'Header', formKey = 'header',
help = 'Text to show in header',
limit = 64,
shadowText = "xxxx's shop",
required = false,
shadowText = "xxxx's shop",
required = false,
},
[4] = UI.Checkbox {
formLabel = 'Is private key', formKey = 'isPrivateKey',
help = 'Password is in private key format',
limit = 64,
},
[5] = UI.Chooser {
width = 9,
formLabel = 'Font Size', formKey = 'textScale',
nochoice = 'Small',
choices = {
{ name = 'Small', value = .5 },
{ name = 'Large', value = 1 },
},
help = 'Adjust text scaling',
},
},
[5] = UI.Chooser {
width = 9,
formLabel = 'Font Size', formKey = 'textScale',
nochoice = 'Small',
choices = {
{ name = 'Small', value = .5 },
{ name = 'Large', value = 1 },
},
help = 'Adjust text scaling',
},
},
}
function wizardPage:setNode(node)
self.form:setValues(node)
self.form:setValues(node)
end
function wizardPage:validate()
return self.form:save()
return self.form:save()
end
function wizardPage:saveNode(node)
os.queueEvent('shop_restart', node)
os.queueEvent('shop_restart', node)
end
function wizardPage:isValidType(node)
local m = device[node.name]
return m and m.type == 'monitor' and {
name = 'Store Front',
value = 'shop',
category = 'display',
help = 'Add a store front display'
}
local m = device[node.name]
return m and m.type == 'monitor' and {
name = 'Store Front',
value = 'shop',
category = 'display',
help = 'Add a store front display'
}
end
function wizardPage:isValidFor(node)
return node.mtype == 'shop'
return node.mtype == 'shop'
end
UI:getPage('nodeWizard').wizard:add({ storeFront = wizardPage })

View File

@@ -7,69 +7,69 @@ local os = _G.os
local config = Config.load('shop')
local shopTab = UI.Tab {
tabTitle = 'Store',
index = 2,
form = UI.Form {
x = 2, ex = -2, y = 1, ey = -2,
manualControls = true,
[1] = UI.TextEntry {
formLabel = 'Name', formKey = 'name',
help = 'Unique name used when paying for an item',
required = true,
width = 12,
limit = 64,
},
[2] = UI.TextEntry {
width = 6,
formLabel = 'Price', formKey = 'price',
help = 'Per item cost',
required = true,
validate = 'numeric',
},
[3] = UI.TextEntry {
limit = 64,
formLabel = 'Extra Info', formKey = 'info',
help = 'Additional info to display for item',
},
clearButton = UI.Button {
x = 2, y = -2,
event = 'clear',
text = 'Remove',
},
updateButton = UI.Button {
x = -12, y = -2,
event = 'update',
text = 'Update',
},
},
tabTitle = 'Store',
index = 2,
form = UI.Form {
x = 2, ex = -2, y = 1, ey = -2,
manualControls = true,
[1] = UI.TextEntry {
formLabel = 'Name', formKey = 'name',
help = 'Unique name used when paying for an item',
required = true,
width = 12,
limit = 64,
},
[2] = UI.TextEntry {
width = 6,
formLabel = 'Price', formKey = 'price',
help = 'Per item cost',
required = true,
validate = 'numeric',
},
[3] = UI.TextEntry {
limit = 64,
formLabel = 'Extra Info', formKey = 'info',
help = 'Additional info to display for item',
},
clearButton = UI.Button {
x = 2, y = -2,
event = 'clear',
text = 'Remove',
},
updateButton = UI.Button {
x = -12, y = -2,
event = 'update',
text = 'Update',
},
},
}
function shopTab:setItem(item)
self.item = item
self.form:setValues(config[item.key] or { })
self.item = item
self.form:setValues(config[item.key] or { })
end
function shopTab:eventHandler(event)
if event.type == 'clear' then
self.form:setValues({ })
config[self.item.key] = nil
Config.update('shop', config)
os.queueEvent('shop_refresh')
self.form:draw()
if event.type == 'clear' then
self.form:setValues({ })
config[self.item.key] = nil
Config.update('shop', config)
os.queueEvent('shop_refresh')
self.form:draw()
elseif event.type == 'update' then
if self.form:save() then
Map.removeMatches(config, { name = self.form.values.name })
config[self.item.key] = self.form.values
Config.update('shop', config)
os.queueEvent('shop_refresh')
self:emit({ type = 'success_message', message = 'Updated' })
end
elseif event.type == 'update' then
if self.form:save() then
Map.removeMatches(config, { name = self.form.values.name })
config[self.item.key] = self.form.values
Config.update('shop', config)
os.queueEvent('shop_refresh')
self:emit({ type = 'success_message', message = 'Updated' })
end
else
return
end
return true
else
return
end
return true
end
return { itemTab = shopTab }

View File

@@ -15,75 +15,75 @@ local shopTab
--[[ Display ]]--
local function showListing(node)
local mon = node.adapter
local list = Milo:listItems()
local mon = node.adapter
local list = Milo:listItems()
mon.clear()
local i = 1
mon.clear()
local i = 1
for k,v in pairs(config) do
local item = list[k]
if item and item.count > 0 then
mon.setCursorPos(1, i)
mon.write(string.format('%d %s: %d kst', v.count, v.displayName, v.price))
mon.setCursorPos(1, i + 1)
mon.write(v.info)
i = i + 2
end
end
for k,v in pairs(config) do
local item = list[k]
if item and item.count > 0 then
mon.setCursorPos(1, i)
mon.write(string.format('%d %s: %d kst', v.count, v.displayName, v.price))
mon.setCursorPos(1, i + 1)
mon.write(v.info)
i = i + 2
end
end
end
-- everything below is important to keep
local function startShop(node)
if shopTab then
multishell.terminate(shopTab)
end
shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password)
if shopTab then
multishell.terminate(shopTab)
end
shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password)
end
-- node has been reconfigured
Event.on('shop_restart', function(_, node)
startShop(node)
startShop(node)
end)
-- milo is being terminated
Event.on('terminate', function()
if shopTab then
multishell.terminate(shopTab)
shopTab = nil
end
if shopTab then
multishell.terminate(shopTab)
shopTab = nil
end
end)
-- called when an item to sell has been changed
Event.on('shop_refresh', function()
config = Config.load('shop')
config = Config.load('shop')
end)
-- called from the shop when an item has been purchased
Event.on('shop_provide', function(_, item, quantity, uid)
Milo:queueRequest({ }, function()
local count = Milo:eject(itemDB:splitKey(item), quantity)
os.queueEvent('shop_provided', uid, count)
end)
Milo:queueRequest({ }, function()
local count = Milo:eject(itemDB:splitKey(item), quantity)
os.queueEvent('shop_provided', uid, count)
end)
end)
--[[ Task ]]--
local StoreTask = {
name = 'shop',
priority = 30,
name = 'shop',
priority = 30,
}
function StoreTask:cycle(context)
local node = context.storage:filterActive('shop')()
if node then
-- a monitor has been configured
if not shopTab then
-- first time running
startShop(node)
end
local node = context.storage:filterActive('shop')()
if node then
-- a monitor has been configured
if not shopTab then
-- first time running
startShop(node)
end
showListing(node)
end
showListing(node)
end
end
Milo:registerTask(StoreTask)

View File

@@ -15,205 +15,205 @@ local config = Config.load('shop')
local shopTab
local function startShop(node)
if shopTab then
multishell.terminate(shopTab)
end
shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password, node.isPrivateKey and 'true')
if shopTab then
multishell.terminate(shopTab)
end
shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password, node.isPrivateKey and 'true')
end
-- node has been reconfigured
Event.on('shop_restart', function(_, node)
startShop(node)
startShop(node)
end)
-- milo is being terminated
Event.on('terminate', function()
if shopTab then
multishell.terminate(shopTab)
shopTab = nil
end
if shopTab then
multishell.terminate(shopTab)
shopTab = nil
end
end)
--[[ Display ]]--
local function createPage(node)
local monitor = UI.Device {
device = node.adapter,
textScale = node.textScale or .5,
}
local monitor = UI.Device {
device = node.adapter,
textScale = node.textScale or .5,
}
function monitor:resize()
self.textScale = node.textScale or .5
UI.Device.resize(self)
end
function monitor:resize()
self.textScale = node.textScale or .5
UI.Device.resize(self)
end
local page = UI.Page {
parent = monitor,
header = UI.Window {
backgroundColor = colors.cyan,
ey = 3,
},
grid = UI.Grid {
y = 4, ey = -7,
headerHeight = 3,
headerBackgroundColor = colors.gray,
backgroundSelectedColor = colors.black,
unfocusedBackgroundSelectedColor = colors.gray,
columns = {
{ heading = 'Stock', key = 'count', width = 6, align = 'right' },
{ heading = 'Name', key = 'displayName' },
{ heading = ' Price', key = 'price', width = 9, align = 'right' },
{ heading = 'Address', key = 'address', width = 12 },
},
sortColumn = 'displayName',
},
footer = UI.Window {
y = -6,
backgroundColor = colors.gray,
prevButton = UI.Button {
x = 2, y = 3, height = 3, width = 5,
event = 'previous',
backgroundColor = colors.lightGray,
text = ' \017 ',
},
nextButton = UI.Button {
x = -6, y = 3, height = 3, width = 5,
event = 'next',
backgroundColor = colors.lightGray,
text = ' \016 ',
},
info = UI.Window {
x = 9, ex = -9,
textColor = colors.white,
}
},
timestamp = os.clock(),
}
local page = UI.Page {
parent = monitor,
header = UI.Window {
backgroundColor = colors.cyan,
ey = 3,
},
grid = UI.Grid {
y = 4, ey = -7,
headerHeight = 3,
headerBackgroundColor = colors.gray,
backgroundSelectedColor = colors.black,
unfocusedBackgroundSelectedColor = colors.gray,
columns = {
{ heading = 'Stock', key = 'count', width = 6, align = 'right' },
{ heading = 'Name', key = 'displayName' },
{ heading = ' Price', key = 'price', width = 9, align = 'right' },
{ heading = 'Address', key = 'address', width = 12 },
},
sortColumn = 'displayName',
},
footer = UI.Window {
y = -6,
backgroundColor = colors.gray,
prevButton = UI.Button {
x = 2, y = 3, height = 3, width = 5,
event = 'previous',
backgroundColor = colors.lightGray,
text = ' \017 ',
},
nextButton = UI.Button {
x = -6, y = 3, height = 3, width = 5,
event = 'next',
backgroundColor = colors.lightGray,
text = ' \016 ',
},
info = UI.Window {
x = 9, ex = -9,
textColor = colors.white,
}
},
timestamp = os.clock(),
}
function page.header:draw()
self:clear()
if node.header then
self:centeredWrite(2, node.header, nil, colors.white)
end
self:write(self.width - 15, 3, 'powered by Milo', nil, colors.gray)
end
function page.header:draw()
self:clear()
if node.header then
self:centeredWrite(2, node.header, nil, colors.white)
end
self:write(self.width - 15, 3, 'powered by Milo', nil, colors.gray)
end
function page.footer.info:draw()
self:clear()
local selected = page.grid:getSelected()
function page.footer.info:draw()
self:clear()
local selected = page.grid:getSelected()
if selected then
if selected.info then
self:centeredWrite(2, selected.info)
end
if selected then
if selected.info then
self:centeredWrite(2, selected.info)
end
self:centeredWrite(4, 'To purchase:')
self:centeredWrite(5, string.format('/pay %s@%s.kst <amount>', selected.name, node.domain))
end
end
self:centeredWrite(4, 'To purchase:')
self:centeredWrite(5, string.format('/pay %s@%s.kst <amount>', selected.name, node.domain))
end
end
function page.grid:getRowTextColor(row, selected)
if selected then
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
function page.grid:getRowTextColor(row, selected)
if selected then
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
function page.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count) .. ' '
row.price = string.format('%s kst ', row.price)
row.address = row.name
return row
end
function page.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count) .. ' '
row.price = string.format('%s kst ', row.price)
row.address = row.name
return row
end
function page:eventHandler(event)
if event.type == 'next' then
self.grid:emit({ type = 'scroll_down' })
function page:eventHandler(event)
if event.type == 'next' then
self.grid:emit({ type = 'scroll_down' })
elseif event.type == 'previous' then
self.grid:emit({ type = 'scroll_up' })
elseif event.type == 'previous' then
self.grid:emit({ type = 'scroll_up' })
elseif event.type == 'grid_focus_row' then
self.footer:draw()
elseif event.type == 'grid_focus_row' then
self.footer:draw()
else
return UI.Page.eventHandler(self, event)
end
else
return UI.Page.eventHandler(self, event)
end
Event.onTimeout(.1, function()
self:setFocus(self.grid)
self:sync()
end)
return true
end
Event.onTimeout(.1, function()
self:setFocus(self.grid)
self:sync()
end)
return true
end
function page:refresh()
local list = Milo:listItems()
self.grid.values = { }
for k,v in pairs(config) do
local item = list[k]
if item and item.count > 0 then
table.insert(self.grid.values, {
displayName = item.displayName,
count = item.count,
name = v.name,
price = v.price,
info = v.info,
})
end
end
self.grid:update()
self.grid:draw()
end
function page:refresh()
local list = Milo:listItems()
self.grid.values = { }
for k,v in pairs(config) do
local item = list[k]
if item and item.count > 0 then
table.insert(self.grid.values, {
displayName = item.displayName,
count = item.count,
name = v.name,
price = v.price,
info = v.info,
})
end
end
self.grid:update()
self.grid:draw()
end
function page:update()
page:refresh()
page:sync()
end
function page:update()
page:refresh()
page:sync()
end
local chars = { '\183', '\7', '\186', '\7' }
Event.onInterval(1, function()
local ch = chars[math.floor(os.clock() % #chars) + 1]
page.header:write(2, 2, ch)
page.header:write(page.header.width - 1, 2, ch)
page:sync()
end)
local chars = { '\183', '\7', '\186', '\7' }
Event.onInterval(1, function()
local ch = chars[math.floor(os.clock() % #chars) + 1]
page.header:write(2, 2, ch)
page.header:write(page.header.width - 1, 2, ch)
page:sync()
end)
UI:setPage(page)
return page
UI:setPage(page)
return page
end
local pages = { }
-- called when an item to sell has been changed
Event.on('shop_refresh', function()
config = Config.load('shop')
config = Config.load('shop')
end)
-- called from the shop when an item has been purchased
Event.on('shop_provide', function(_, item, quantity, uid)
Milo:queueRequest({ }, function()
local count = Milo:eject(itemDB:splitKey(item), quantity)
os.queueEvent('shop_provided', uid, count)
Sound.play('entity.player.levelup')
end)
Milo:queueRequest({ }, function()
local count = Milo:eject(itemDB:splitKey(item), quantity)
os.queueEvent('shop_provided', uid, count)
Sound.play('entity.player.levelup')
end)
end)
--[[ Task ]]--
local StoreTask = {
name = 'shop',
priority = 30,
name = 'shop',
priority = 30,
}
function StoreTask:cycle(context)
for node in context.storage:filterActive('shop') do
if not pages[node.name] then
startShop(node)
pages[node.name] = createPage(node)
end
-- update the display
pages[node.name]:update()
end
for node in context.storage:filterActive('shop') do
if not pages[node.name] then
startShop(node)
pages[node.name] = createPage(node)
end
-- update the display
pages[node.name]:update()
end
end
Milo:registerTask(StoreTask)

View File

@@ -27,7 +27,7 @@ w.init(jua)
k.init(jua, json, w, r)
local function Syntax()
error('Syntax: swshop [domain] [password | privateKey] [isPrivateKey]')
error('Syntax: swshop [domain] [password | privateKey] [isPrivateKey]')
end
local args = { ... }
@@ -38,125 +38,125 @@ local address = k.makev2address(privatekey)
local transactions = Util.readTable('/usr/swshop.log') or { }
jua.on("terminate", function()
rs.setOutput('top', false)
jua.stop()
_G.printError("Terminated")
rs.setOutput('top', false)
jua.stop()
_G.printError("Terminated")
end)
local function getItemDetails(item)
local f = fs.open('usr/config/shop', "r")
if f then
local t = f.readAll()
f.close()
t = textutils.unserialize(t)
for key, v in pairs(t) do
if v.name == item then
return key, v.price
end
end
end
local f = fs.open('usr/config/shop', "r")
if f then
local t = f.readAll()
f.close()
t = textutils.unserialize(t)
for key, v in pairs(t) do
if v.name == item then
return key, v.price
end
end
end
end
local function logTransaction(transaction, details)
transaction = Util.shallowCopy(transaction)
Util.merge(transaction, details)
table.insert(transactions, transaction)
Util.writeTable('/usr/swshop.log', transactions)
transaction = Util.shallowCopy(transaction)
Util.merge(transaction, details)
table.insert(transactions, transaction)
Util.writeTable('/usr/swshop.log', transactions)
end
local function handleTransaction(transaction)
local from = transaction.from
local to = transaction.to
local value = transaction.value
if to ~= address or not transaction.metadata then return end
local from = transaction.from
local to = transaction.to
local value = transaction.value
if to ~= address or not transaction.metadata then return end
local metadata = k.parseMeta(transaction.metadata)
if not metadata.domain or metadata.domain ~= domain then return end
local metadata = k.parseMeta(transaction.metadata)
if not metadata.domain or metadata.domain ~= domain then return end
local recipient = metadata.meta and (metadata.meta["return"] or from) or from
print("Handling transaction from ", recipient)
print('purchase: ' .. tostring(metadata.name))
print('value: ' .. value)
local recipient = metadata.meta and (metadata.meta["return"] or from) or from
print("Handling transaction from ", recipient)
print('purchase: ' .. tostring(metadata.name))
print('value: ' .. value)
local t = {
to = transaction.to,
from = transaction.from,
value = transaction.value,
id = metadata.name,
}
local t = {
to = transaction.to,
from = transaction.from,
value = transaction.value,
id = metadata.name,
}
local function refundTransaction(amount, reason)
print("Refunding to ", recipient)
await(k.makeTransaction, privatekey, recipient, amount, reason)
logTransaction(t, { refund = amount, reason = reason })
end
local function refundTransaction(amount, reason)
print("Refunding to ", recipient)
await(k.makeTransaction, privatekey, recipient, amount, reason)
logTransaction(t, { refund = amount, reason = reason })
end
t.itemId, t.price = getItemDetails(metadata.name)
if not t.itemId or not t.price then
print('invalid item')
logTransaction(t, { reason = 'invalid item' })
--return refundTransaction(value, "error=Item specified is not valid")
return -- there could be multiple stores...
end
t.itemId, t.price = getItemDetails(metadata.name)
if not t.itemId or not t.price then
print('invalid item')
logTransaction(t, { reason = 'invalid item' })
--return refundTransaction(value, "error=Item specified is not valid")
return -- there could be multiple stores...
end
if value < t.price then
print('value too low')
return refundTransaction(value, "error=Please pay the price listed on-screen.")
end
if value < t.price then
print('value too low')
return refundTransaction(value, "error=Please pay the price listed on-screen.")
end
local count = math.floor(value / t.price)
local uid = math.random()
print(string.format('requesting %d of %s', count, t.itemId))
os.queueEvent('shop_provide', t.itemId, count, uid)
local timerId = os.startTimer(60)
while true do
local e, p1, p2 = os.pullEvent()
if e == 'timer' and p1 == timerId then
print('timed out waiting for provide')
refundTransaction(value, "error=Timed out attempting to provide items")
break
local count = math.floor(value / t.price)
local uid = math.random()
print(string.format('requesting %d of %s', count, t.itemId))
os.queueEvent('shop_provide', t.itemId, count, uid)
local timerId = os.startTimer(60)
while true do
local e, p1, p2 = os.pullEvent()
if e == 'timer' and p1 == timerId then
print('timed out waiting for provide')
refundTransaction(value, "error=Timed out attempting to provide items")
break
elseif e == 'shop_provided' and p1 == uid then
local extra = value - (t.price * p2)
logTransaction(t, { purchased = p2 })
if chat and chat.tell then
local msg = string.format('PURCHASE: %s bought %d %s for %s',
recipient, p2, t.itemId, t.price * p2)
pcall(chat.tell, msg)
end
if extra > 0 then
print('extra: ' .. extra)
refundTransaction(extra, "message=Here's your change!")
end
break
end
end
elseif e == 'shop_provided' and p1 == uid then
local extra = value - (t.price * p2)
logTransaction(t, { purchased = p2 })
if chat and chat.tell then
local msg = string.format('PURCHASE: %s bought %d %s for %s',
recipient, p2, t.itemId, t.price * p2)
pcall(chat.tell, msg)
end
if extra > 0 then
print('extra: ' .. extra)
refundTransaction(extra, "message=Here's your change!")
end
break
end
end
end
local function connect()
print('opening store for: ' .. domain)
print('opening store for: ' .. domain)
local success, ws = await(k.connect, privatekey)
assert(success, "Failed to get websocket URL")
local success, ws = await(k.connect, privatekey)
assert(success, "Failed to get websocket URL")
print("Connected to websocket.")
rs.setOutput('top', true)
print("Connected to websocket.")
rs.setOutput('top', true)
success = await(ws.subscribe, "ownTransactions", function(data)
local transaction = data.transaction
handleTransaction(transaction)
end)
assert(success, "Failed to subscribe to event")
success = await(ws.subscribe, "ownTransactions", function(data)
local transaction = data.transaction
handleTransaction(transaction)
end)
assert(success, "Failed to subscribe to event")
end
local s, m = pcall(function()
jua.go(function()
print("Ready")
connect()
end)
jua.go(function()
print("Ready")
connect()
end)
end)
rs.setOutput('top', false)
if not s then
error(m, 2)
error(m, 2)
end

View File

@@ -2,134 +2,134 @@ local jua = nil
local idPatt = "#R%d+"
if not ((socket and socket.websocket) or http.websocketAsync) then
error("You do not have CC:Tweaked/CCTweaks installed or you are not on the latest version.")
error("You do not have CC:Tweaked/CCTweaks installed or you are not on the latest version.")
end
local newws = socket and socket.websocket or http.websocketAsync
local async
if socket and socket.websocket then
async = false
async = false
else
async = true
async = true
end
callbackRegistry = {}
wsRegistry = {}
local function gfind(str, patt)
local t = {}
for found in str:gmatch(patt) do
table.insert(t, found)
end
local t = {}
for found in str:gmatch(patt) do
table.insert(t, found)
end
if #t > 0 then
return t
else
return nil
end
if #t > 0 then
return t
else
return nil
end
end
local function findID(url)
local found = gfind(url, idPatt)
local id = tonumber(found[#found]:sub(found[#found]:find("%d+")))
return id
local found = gfind(url, idPatt)
local id = tonumber(found[#found]:sub(found[#found]:find("%d+")))
return id
end
local function newID()
return #callbackRegistry + 1
return #callbackRegistry + 1
end
local function trimID(url)
local found = gfind(url, idPatt)
local s, e = url:find(found[#found])
return url:sub(1, s-1)
local found = gfind(url, idPatt)
local s, e = url:find(found[#found])
return url:sub(1, s-1)
end
function open(callback, url, headers)
local id
if async then
id = newID()
end
local newUrl
if async then
newUrl = url .. "#R" .. id
newws(newUrl, headers)
else
if headers then
error("Websocket headers not supported under CCTweaks")
end
local ws = newws(url)
ws.send = ws.write
id = ws.id()
wsRegistry[id] = ws
end
callbackRegistry[id] = callback
return id
local id
if async then
id = newID()
end
local newUrl
if async then
newUrl = url .. "#R" .. id
newws(newUrl, headers)
else
if headers then
error("Websocket headers not supported under CCTweaks")
end
local ws = newws(url)
ws.send = ws.write
id = ws.id()
wsRegistry[id] = ws
end
callbackRegistry[id] = callback
return id
end
function init(jua)
jua = jua
if async then
jua.on("websocket_success", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].success then
callbackRegistry[id].success(id, handle)
end
end)
jua = jua
if async then
jua.on("websocket_success", function(event, url, handle)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].success then
callbackRegistry[id].success(id, handle)
end
end)
jua.on("websocket_failure", function(event, url)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].failure then
callbackRegistry[id].failure(id)
end
table.remove(callbackRegistry, id)
end)
jua.on("websocket_failure", function(event, url)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].failure then
callbackRegistry[id].failure(id)
end
table.remove(callbackRegistry, id)
end)
jua.on("websocket_message", function(event, url, data)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].message then
callbackRegistry[id].message(id, data)
end
end)
jua.on("websocket_message", function(event, url, data)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].message then
callbackRegistry[id].message(id, data)
end
end)
jua.on("websocket_closed", function(event, url)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].closed then
callbackRegistry[id].closed(id)
end
table.remove(callbackRegistry, id)
end)
else
jua.on("socket_connect", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].success then
callbackRegistry[id].success(id, wsRegistry[id])
end
end)
jua.on("websocket_closed", function(event, url)
local id = findID(url)
if id and callbackRegistry[id] and callbackRegistry[id].closed then
callbackRegistry[id].closed(id)
end
table.remove(callbackRegistry, id)
end)
else
jua.on("socket_connect", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].success then
callbackRegistry[id].success(id, wsRegistry[id])
end
end)
jua.on("socket_error", function(event, id, msg)
if id and callbackRegistry[id] and callbackRegistry[id].failure then
callbackRegistry[id].failure(id, msg)
end
table.remove(callbackRegistry, id)
end)
jua.on("socket_error", function(event, id, msg)
if id and callbackRegistry[id] and callbackRegistry[id].failure then
callbackRegistry[id].failure(id, msg)
end
table.remove(callbackRegistry, id)
end)
jua.on("socket_message", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].message then
local data = wsRegistry[id].read()
callbackRegistry[id].message(id, data)
end
end)
jua.on("socket_message", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].message then
local data = wsRegistry[id].read()
callbackRegistry[id].message(id, data)
end
end)
jua.on("socket_closed", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].closed then
callbackRegistry[id].closed(id)
end
table.remove(callbackRegistry, id)
end)
end
jua.on("socket_closed", function(event, id)
if id and callbackRegistry[id] and callbackRegistry[id].closed then
callbackRegistry[id].closed(id)
end
table.remove(callbackRegistry, id)
end)
end
end
return {
open = open,
init = init
open = open,
init = init
}