spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access
This commit is contained in:
@@ -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())
|
||||
|
||||
148
swshop/jua.lua
148
swshop/jua.lua
@@ -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
|
||||
}
|
||||
532
swshop/k.lua
532
swshop/k.lua
@@ -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
|
||||
}
|
||||
76
swshop/r.lua
76
swshop/r.lua
@@ -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
|
||||
}
|
||||
@@ -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 })
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
186
swshop/w.lua
186
swshop/w.lua
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user