local pkey = settings.get("carrotpay.private_key") if not switchcraft then error("Must run on SwitchCraft 3") end sleep(1) local sha256 = require("sha256") if not chatbox or not chatbox.hasCapability("command") or not chatbox.hasCapability("tell") then term.setBackgroundColor(colors.black) term.clear() term.setCursorPos(1,1) term.setTextColor(colors.white) print("Hey! it seems like you haven't registered chatbox") print("If you don't have a chatbox key, run this command:") term.setTextColor(colors.green) print("/chatbox register") term.setTextColor(colors.white) print("Then copy that key and paste it here") write("Alternatively, run ") term.setTextColor(colors.blue) print("chatbox register ") term.setTextColor(colors.white) local cbkey = read() settings.set("chatbox.license_key",cbkey) settings.save(".settings") print("Key set. Press Any Key to reboot") os.pullEvent("key") os.reboot() end if not pkey then term.setBackgroundColor(colors.black) term.setTextColor(colors.white) term.clear() term.setCursorPos(1,1) term.setBackgroundColor(colors.lightGray) print("Initial Setup ") term.setBackgroundColor(colors.black) print("First, CarrotPay needs your private key") print("for the wallet you wish to use with CarrotPay") print("Please enter your private key (not password) here") local ipkey = read() settings.set("carrotpay.private_key",ipkey) settings.save(".settings") term.clear() term.setCursorPos(1,1) term.setBackgroundColor(colors.lightGray) print("Initial Setup ") term.setBackgroundColor(colors.black) print("Do you want to use our default startup script?") print("(Y/N)") print("Typing anything other than Y will be treated as no") term.setTextColor(colors.red) print("WARNING! Saying yes will OVERWRITE startup.lua") term.setTextColor(colors.white) local event = {os.pullEvent("key")} if event[2] == keys.y then print("Overwrote startup file.") fs.delete("startup.lua") local file = fs.open("startup.lua","w") file.write([=[while true do shell.run("]=]..shell.getRunningProgram()..[=[") sleep(2) end]=]) file.close() end print("CarrotPay Setup Requires a reboot.") print("Press any key to reboot") os.pullEvent("key") os.reboot() end prt = require("cc.pretty").pretty_print local owner =chatbox.getLicenseOwner() local 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) end local expect = require("cc.expect").expect local function split(inputstr, sep) expect(1,inputstr,"string") expect(1,sep,"string","nil") sep = sep or "," local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end local function make_address(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 end local address = make_address(pkey) local function get_balance(addr) os.queueEvent("get_balance",addr) _,balance,err = os.pullEvent("balance") if balance == "error" then prt(err) return -1 end return balance end local function pay(to,amt,mta) if not mta then mta = "" end mta = "username="..owner..";"..mta amt = tonumber(amt) local bal = get_balance(address) if amt > bal then chatbox.tell(owner,"Transaction failed. Not enough Krist","&6CarrotPay") return false else os.queueEvent("make_transaction",to,amt,mta) _,ok = os.pullEvent("transaction_complete") return ok end end local function handleWebSockets() local id = -1 local r = http.post("https://krist.dev/ws/start","{\"privatekey\":\""..pkey.."\"}",{["content-type"]="application/json"}) local resp = textutils.unserialiseJSON(r.readAll()) r.close() r = nil if resp.ok then socket = http.websocket(resp.url) print("Connected to Krist Websockets") id = id + 1 socket.send('{\"id\":'..id ..',\"type\":\"subscribe\",\"event\":\"ownTransactions\"}') while true do event = {os.pullEvent()} if event[1] == "websocket_message" then if event[2] == resp.url then wsevent = textutils.unserialiseJSON(event[3]) if wsevent.event == "transaction" and wsevent.transaction.to == address then local from = wsevent.transaction.from local hasMessage = false local hasError = false local hasUsername = false local message = "" err = "" if wsevent.transaction.metadata then mta = split(wsevent.transaction.metadata,";") for i,p in pairs(mta) do if p:match("message") then message = split(p,"=")[2] hasMessage = true end if p:match("error") then err = split(p,"=")[2] hasError = true end if p:match("username") and not hasUsername then from = split(p,"=")[2].." ("..wsevent.transaction.from..")" hasUsername = true end end end msg2 = "You have received K"..wsevent.transaction.value.." from "..from if hasMessage or hasError then if hasMessage then msg2 = msg2.."\nMessage: "..message end if hasError then msg2 = msg2.."\nError: "..err end end chatbox.tell(owner,msg2,"&6CarrotPay") elseif wsevent.type == "keepalive" or wsevent.type == "response" then else end end elseif event[1] == "get_balance" then id = id + 1 local rq = { id = id, type="address", address=event[2] } socket.send(textutils.serialiseJSON(rq)) rspt =socket.receive() rsp = textutils.unserialiseJSON(rspt) if rsp.ok then os.queueEvent("balance",rsp.address.balance) else os.queueEvent("balance","error",rsp) end elseif event[1] == "make_transaction" then id = id + 1 local rq = { id = id, type="make_transaction", to = event[2], amount = event[3], metadata = event[4], } socket.send(textutils.serialiseJSON(rq)) local c repeat c = socket.receive() c = textutils.unserialiseJSON(c) until c.type == "response" os.queueEvent("transaction_complete",c.ok) end end end end local function handleCommands() while true do local command = {os.pullEvent("command")} if command[3] == "pay" and command[5].ownerOnly then if command[4][1] == "--update" then shell.run("wget https://raw.githubusercontent.com/scmcgowen/carrotpay/main/carrotpay.lua "..shell.getRunningProgram()) os.reboot() end if not command[4][1] then chatbox.tell(command[2],"Specify who to pay","&6CarrotPay") elseif not command[4][2] then chatbox.tell(command[2],"Specify how much to pay","&6CarrotPay") else local c = false command_copy = {table.unpack(command)} mta = table.concat({select(3, unpack(command_copy[4]))}, " ") local s = command[4][1] if ((s:match("^k[a-z0-9]+$") or s:match("^[a-f0-9]+$")) and #s == 10) or (s:match("^[a-z0-9]@[a-z0-9]+$")) or s:match("^[%a%d]+@[%a%d]+%.kst$") or s:match("^[%a%d]+%.kst") then c = pay(s,command[4][2],mta) if c then chatbox.tell(command[2],"Paid <:kst:665040403224985611>"..command[4][2].." to "..s,"&6CarrotPay") end elseif s:match("^[a-zA-Z0-9_]+$") and #s <16 then c = pay(s.."@sc.kst",command[4][2],mta) if c then chatbox.tell(command[2],"Paid <:kst:665040403224985611>"..command[4][2] .. " to "..s,"&6CarrotPay") end else chatbox.tell(command[2],"Invalid Krist Address","&6CarrotPay") end if not c then chatbox.tell(command[2],"There was an error.","&6CarrotPay") end end elseif command[3] == "request" and command[5].ownerOnly then msg = table.concat({select(3,unpack(command[4]))}, " ") if msg == "" then chatbox.tell(command[4][1],owner.." wants <:kst:665040403224985611>"..command[4][2].." from you.\nPay to "..address,"&6CarrotPay") else chatbox.tell(command[4][1],owner.." wants <:kst:665040403224985611>"..command[4][2].." from you.\nPay to "..address.."\nMessage: "..msg,"&6CarrotPay") end chatbox.tell(owner,"Requested <:kst:665040403224985611>"..command[4][2].." from "..command[4][1],"&6CarrotPay") elseif (command[3] == "bal" or command[3] == "balance") and command[5].ownerOnly then local addr = command[4][1] or address local bal = get_balance(addr) chatbox.tell(owner,addr.." has a balance of <:kst:665040403224985611>"..bal,"&6CarrotPay") end end end _,e = pcall(parallel.waitForAny,handleWebSockets,handleCommands) if socket then socket.close() end printError(e)