mcdiscordbot/MinecraftDiscordBot/ClientScript.lua
Michael Chen a55af9f667
Increase chunk size to send fewer messages.
Fixed logging lock (causes color issues in parallel env)
2022-01-18 10:43:15 +01:00

219 lines
6.8 KiB
Lua

local secretToken = "$TOKEN"
local connectionUri = "$HOST"
local waitSeconds = 5
-- https://github.com/cc-tweaked/CC-Tweaked/blob/9cf70b10effeeed23e0e9c537bbbe0b2ff0d1a0f/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java#L29
-- Chunk size must be less than packet size (type reply, success, chunkids, content: chunk) 16 kb for buffer
local maxMessageSize = (128 - 16) * 1024
local function chunkString(value, chunkSize)
if not chunkSize then chunkSize = maxMessageSize end
local length = value:len()
local total = math.ceil(length / chunkSize)
local chunks = {}
if length == 0 then
total = 1
chunks[1] = ""
else
local i = 1
for i=1,total do
local pos = 1 + ((i - 1) * chunkSize)
chunks[i] = value:sub(pos, pos + chunkSize - 1)
end
end
return total, chunks
end
local function sendJson(socket, message)
return socket.send(textutils.serializeJSON(message))
end
local function sendResponse(socket, id, result, success)
if success == nil then success = 0 end
local total, chunks = chunkString(result)
for i, chunk in pairs(chunks) do
sendJson(socket, { type = "reply", id = id, result = chunk, chunk = i, total = total, success = success })
end
end
-- error: no rs system
-- return rssystem rs
local function getPeripheral(name)
local dev = peripheral.find(name)
if not dev then error({message = "No peripheral '"..name.."' attached to the computer!"}) end
return dev
end
local function runRsCommand(params)
local script, reason = loadstring("local rs = peripheral.find(\"rsBridge\") if not rs then error({message = \"RS Bridge is not attached!\"}) end return rs."..params.command)
if not script then error({message = "Invalid command: "..reason.."!"}) end
local result = table.pack(pcall(script))
local success = result[1]
if not success then error({message = "Command execution failed: "..result[2].."!"}) end
local retvals = {}
retvals.n = result.n - 1
for i=1,retvals.n do retvals[tostring(i)] = result[i + 1] end
return textutils.serializeJSON(retvals)
end
-- error: any error during execution
-- return string result
local function getResponse(parsed)
if parsed.method == "energyusage" then
return tostring(getPeripheral("rsBridge").getEnergyUsage())
elseif parsed.method == "energystorage" then
return tostring(getPeripheral("rsBridge").getEnergyStorage())
elseif parsed.method == "listitems" then
return textutils.serializeJSON(getPeripheral("rsBridge").listItems())
elseif parsed.method == "listfluids" then
return textutils.serializeJSON(getPeripheral("rsBridge").listFluids())
elseif parsed.method == "craft" then
return tostring(getPeripheral("rsBridge").craftItem(parsed.params))
elseif parsed.method == "getitem" then
local item = getPeripheral("rsBridge").getItem(parsed.params)
if not item then error({message = "Requested item not found!"}) end
return textutils.serializeJSON(item)
elseif parsed.method == "command" then
return runRsCommand(parsed.params)
elseif parsed.method == "getonline" then
return textutils.serializeJSON(getPeripheral("playerDetector").getOnlinePlayers())
elseif parsed.method == "whereis" then
local pos = getPeripheral("playerDetector").getPlayerPos(parsed.params.username)
if not pos then return "null" end
return textutils.serializeJSON(pos)
elseif parsed.method == "send" then
if not parsed.params.username then
getPeripheral("chatBox").sendMessage(parsed.params.message, parsed.params.prefix)
else
getPeripheral("chatBox").sendMessageToPlayer(parsed.params.message, parsed.params.username, parsed.params.prefix)
end
return "true"
end
error({message = "No message handler for method: "..parsed.method.."!"})
end
local function logJSON(json, prefix)
if not prefix then prefix = "" end
for k,v in pairs(json) do
local key = prefix..k
if type(v) == "table" then
logJSON(v, key..".")
else
print(key, "=", textutils.serializeJSON(v))
end
end
end
-- return bool success
local function handleMessage(socket, message)
local parsed, reason = textutils.unserializeJSON(message)
if not parsed then
print("Received message:", message)
printError("Message could not be parsed:", reason)
return false
end
pcall(function() print("Received JSON:") logJSON(parsed) end)
if parsed.type == "request" then
local success, result = pcall(function() return getResponse(parsed) end)
if not success then
if not result.message then
sendResponse(socket, parsed.id, result, 2)
else
sendResponse(socket, parsed.id, result.message, 1)
end
else
sendResponse(socket, parsed.id, result, 0)
end
return true
end
printError("Invalid message type:", parsed.type)
return false
end
local function responder(socket)
while true do
local message, binary = socket.receive()
if not not message and not binary then
if message == "outdated" then
printError("Current script is outdated! Please update from the host!")
return
end
handleMessage(socket, message)
end
end
end
local function termWaiter()
os.pullEvent("terminate")
end
local function chatEventListener(socket)
while true do
event, username, message, uuid, hidden = os.pullEvent("chat")
sendJson(socket, {type = "chat", username = username, message = message, uuid = uuid, hidden = hidden})
print("Chat event relayed!")
end
end
local function getPeripheralInfo(side)
return {type = peripheral.getType(side), methods = peripheral.getMethods(side), side = side}
end
local function peripheralDetachEventListener(socket)
while true do
event, side = os.pullEvent("peripheral_detach")
sendJson(socket, {type = "peripheral_detach", side = side})
print("Peripheral was detached!")
end
end
local function peripheralAttachEventListener(socket)
while true do
event, side = os.pullEvent("peripheral")
sendJson(socket, {type = "peripheral", peripheral = getPeripheralInfo(side) })
print("Peripheral was attached!")
end
end
local function eventListeners(socket)
parallel.waitForAny(
termWaiter,
function() chatEventListener(socket) end,
function() peripheralDetachEventListener(socket) end,
function() peripheralAttachEventListener(socket) end
)
end
local function socketClient()
print("Connecting to the socket server at "..connectionUri.."...")
local socket, reason = http.websocket(connectionUri)
if not socket then error("Socket server could not be reached: "..reason) end
print("Connection successful!")
socket.send("login="..secretToken)
parallel.waitForAny(
function() responder(socket) end,
function() eventListeners(socket) end
)
socket.close()
end
local function main()
while true do
local status, error = pcall(socketClient)
if status then break end
printError("An uncaught exception was raised:", error)
printError("Restarting in", waitSeconds, "seconds...")
sleep(waitSeconds)
end
end
local oldPullEvent = os.pullEvent
os.pullEvent = os.pullEventRaw
pcall(main)
os.pullEvent = oldPullEvent