Files
opus-apps/swshop/swshop.lua
Kan18 2f67fb2ef8 Try to update Milo for 1.19
Removes Milo trying to access damage on items (`nil` because of The
Flattening). We might also need to reimplement showing durability in the
item's display name - I got a little too carried away with removing
mentions of "damage". Also renames nbtHash to nbt to be consistent with
new CC:T naming. I tried not to touch anything related to MiloRemote for
now, and there are probably still many bugs remaining that need to be
ironed out. Most of the basic functionality works now, though.
2022-12-24 15:39:14 +04:00

191 lines
5.2 KiB
Lua

local Config = require('opus.config')
local Krist = require('swshop.krist')
local Util = require('opus.util')
local fs = _G.fs
local os = _G.os
local w = require("swshop.w")
local r = require("swshop.r")
local k = require("swshop.k")
local jua = require("swshop.jua")
local await = jua.await
local device = _G.device
local json = { encode=textutils.serializeJSON, decode = textutils.unserialiseJSON }
local rs = _G.rs
local textutils = _G.textutils
local chat = device['plethora:chat']
local storage = Config.load('storage')
Util.each(rs.getSides(), function(side) rs.setOutput(side, false) end)
local defaultKristNode = "https://krist.dev"
local node = ({ ... })[1] or error('Node name is required')
local config = storage[node]
local privatekey = config.isPrivateKey and config.password or Krist.toKristWalletFormat(config.password)
local nodeurl = config.syncNode or defaultKristNode
local address = Krist.makev2address(privatekey)
local rsSide = config.rsSide or 'top'
r.init(jua)
w.init(jua)
k.setNodeUrl(nodeurl:gsub("https?://", ""))
k.init(jua, json, w, r)
jua.on("terminate", function()
rs.setOutput(rsSide, 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
end
end
return nil, {}
end
end
local function logTransaction(transaction, details)
local transactions = Util.readTable('/usr/swshop.log') or { }
transaction = Util.shallowCopy(transaction)
Util.merge(transaction, details)
table.insert(transactions, transaction)
Util.writeTable('/usr/swshop.log', transactions)
os.queueEvent('shop_transaction', transaction)
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 metadata = k.parseMeta(transaction.metadata)
if not metadata.domain or metadata.domain ~= config.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 t = {
to = transaction.to,
from = transaction.from,
value = transaction.value,
txid = transaction.id,
id = metadata.name,
time = math.floor(os.epoch('utc')/1000),
recipient = recipient,
}
local function refundTransaction(amount, reason)
print("Refunding to", recipient)
await(k.makeTransaction, privatekey, recipient, amount, reason)
logTransaction(t, { refund = amount, reason = reason })
end
local id, item = getItemDetails(metadata.name)
t.itemId, t.price = id, tonumber(item.price)
if not t.itemId or not t.price then
print('invalid item')
logTransaction(t, { reason = 'invalid item' })
if config.refundInvalid then
return refundTransaction(value, "error=Item specified is not valid")
end
return -- multiple store setup
end
if value < t.price then
print('value too low')
return refundTransaction(value, "error=Please pay the price listed on-screen.")
end
if item.shares then
local amount = t.value - (t.value % t.price)
for _, share in pairs(item.shares) do
local cut = math.floor(amount * share.rate) -- Don't include fractional Krist
print("Sent shares of "..cut.." to "..share.address)
await(k.makeTransaction, privatekey, share.address, cut, share.meta)
end
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 == 'turtle_inventory' then
os.cancelTimer(timerId)
timerId = os.startTimer(60)
elseif 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 })
local msg = string.format('PURCHASE: %s bought %d %s for %s',
recipient, p2, t.itemId, t.price * p2)
if chat and chat.tell then
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: ' .. config.domain)
print('using address: ' .. address)
local success, ws = await(k.connect, privatekey)
assert(success, "Failed to get websocket URL")
print("Connected to websocket.")
rs.setOutput(rsSide, true)
success = await(ws.subscribe, "ownTransactions", function(data)
local transaction = data.transaction
handleTransaction(transaction)
end)
await(ws.subscribe, "keepalive", function(data)
local state = rs.getOutput(rsSide)
rs.setOutput(rsSide, not state)
end)
assert(success, "Failed to subscribe to event")
end
local s, m = pcall(function()
jua.go(function()
print("Ready")
connect()
end)
end)
rs.setOutput(rsSide, false)
if not s then
error(m, 2)
end