Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- print("========================================")
- print(" BonziWORLD ComputerCraft Bridge")
- print("========================================")
- print("")
- if not http then
- print("[-] HTTP API is disabled.")
- return
- end
- if not http.websocket then
- print("[-] WebSocket API is unavailable.")
- print("[-] You need a newer CC:Tweaked.")
- return
- end
- write("Server URL: ")
- local serverUrl = read()
- write("Room ID: ")
- local roomInput = read()
- write("Bot Name: ")
- local botName = read()
- if not botName or botName == "" then
- botName = "CC_Bot"
- end
- serverUrl = serverUrl:gsub("^https?://", ""):gsub("/+$", "")
- local room = tonumber(roomInput) or roomInput
- local wsUrl = "wss://" .. serverUrl .. "/socket.io/?EIO=4&transport=websocket"
- local ws = nil
- local socketConnected = false
- local loggedIn = false
- local shuttingDown = false
- local messageQueue = {}
- local pingInterval = 25000
- local pingTimeout = 20000
- local APP_HEARTBEAT = 10
- local SEND_DELAY = 0.20
- local LOGIN_DELAY = 1.5
- local reconnectDelay = 2
- local lastPacketTime = 0
- local function now()
- return os.epoch("utc")
- end
- local function socketEvent(eventName, data)
- return "42" .. textutils.serialiseJSON({ eventName, data })
- end
- local function rawSend(packet)
- if not ws or not socketConnected then return false end
- local ok, err = pcall(function()
- ws.send(packet)
- end)
- if not ok then
- print("\n[-] WebSocket send failed:\n " .. tostring(err))
- return false
- end
- return true
- end
- local function sendChat(message)
- if not message or message == "" then return false end
- return rawSend(socketEvent("talk", { text = message, say = message }))
- end
- local function sendApplicationHeartbeat()
- return rawSend(socketEvent("talk", { text = "", say = "" }))
- end
- local function sendLogin()
- print("[>] Login: " .. botName)
- print("[>] Room: " .. tostring(room))
- return rawSend(socketEvent("login", { name = botName, room = room }))
- end
- local function processPacket(packet)
- if not packet then return end
- lastPacketTime = now()
- if packet:sub(1, 1) == "0" then
- local ok, data = pcall(textutils.unserialiseJSON, packet:sub(2))
- if ok and type(data) == "table" then
- pingInterval = tonumber(data.pingInterval) or pingInterval
- pingTimeout = tonumber(data.pingTimeout) or pingTimeout
- print("[+] Engine.IO ping interval: " .. tostring(pingInterval) .. "ms")
- print("[+] Engine.IO ping timeout: " .. tostring(pingTimeout) .. "ms")
- end
- rawSend("40")
- return
- end
- if packet == "2" then
- rawSend("3")
- return
- end
- if packet == "3" then return end
- if packet:sub(1, 1) == "1" then
- print("\n[!] Server closed Engine.IO.")
- socketConnected, loggedIn = false, false
- return
- end
- if packet == "40" then
- if not socketConnected then
- socketConnected = true
- print("[+] Socket.IO connected.")
- end
- return
- end
- if packet:sub(1, 2) == "42" then
- local ok, decoded = pcall(textutils.unserialiseJSON, packet:sub(3))
- if ok and type(decoded) == "table" then
- local eventName, payload = decoded[1], decoded[2]
- if eventName == "talk" and type(payload) == "table" then
- local sender = payload.name or "User"
- local text = payload.text or payload.say or ""
- if text ~= "" and sender ~= botName then
- print("\n[" .. sender .. "]: " .. text)
- write("> ")
- end
- end
- end
- return
- end
- print("[?] " .. packet)
- end
- local function closeSocket()
- socketConnected, loggedIn = false, false
- if ws then
- pcall(function() ws.close() end)
- end
- ws = nil
- end
- local function connect()
- print("\n----------------------------------------")
- print("[*] Connecting...\n[*] " .. serverUrl)
- print("----------------------------------------")
- local newWs, err = http.websocket(wsUrl, {
- ["User-Agent"] = "Mozilla/5.0 (CraftOS)",
- ["Origin"] = "https://" .. serverUrl
- })
- if not newWs then
- print("[-] WebSocket connection failed:\n " .. tostring(err))
- return false
- end
- ws = newWs
- socketConnected, loggedIn = false, false
- lastPacketTime = now()
- print("[+] WebSocket connected.")
- local deadline = now() + 10000
- while now() < deadline do
- local packet = ws.receive(1)
- if packet then
- processPacket(packet)
- if packet:sub(1, 1) == "0" then break end
- end
- end
- if not socketConnected then
- local socketDeadline = now() + 10000
- while now() < socketDeadline and not socketConnected do
- local packet = ws.receive(1)
- if packet then processPacket(packet) end
- end
- end
- if not socketConnected then
- print("[-] Socket.IO connection failed.")
- closeSocket()
- return false
- end
- if not sendLogin() then
- print("[-] Could not send login.")
- closeSocket()
- return false
- end
- print("[*] Waiting for room registration...")
- local loginDeadline = now() + math.floor(LOGIN_DELAY * 1000)
- while now() < loginDeadline do
- local remaining = loginDeadline - now()
- local timeout = math.min(0.25, remaining / 1000)
- if timeout > 0 then
- local packet = ws.receive(timeout)
- if packet then processPacket(packet) end
- end
- end
- loggedIn = true
- print("\n========================================")
- print(" BRIDGE ONLINE")
- print("========================================")
- print("Bot : " .. botName)
- print("Room : " .. tostring(room))
- print("========================================")
- print("")
- return true
- end
- local function inputLoop()
- while not shuttingDown do
- write("> ")
- local message = read()
- if not message then break end
- if message == "exit" then
- shuttingDown = true
- print("\n[*] Shutting down...")
- break
- end
- if message ~= "" then
- table.insert(messageQueue, message)
- print("[queued] " .. tostring(message))
- end
- end
- end
- local function networkLoop()
- local nextHeartbeat = 0
- local nextSend = 0
- while not shuttingDown do
- if not ws or not socketConnected or not loggedIn then
- if connect() then
- reconnectDelay = 2
- nextHeartbeat = now() + (APP_HEARTBEAT * 1000)
- nextSend = 0
- else
- if shuttingDown then break end
- print("[*] Reconnecting in " .. tostring(reconnectDelay) .. " seconds...")
- os.sleep(reconnectDelay)
- reconnectDelay = math.min(reconnectDelay * 2, 30)
- end
- end
- if ws and socketConnected and loggedIn then
- local packet, reason = ws.receive(0.25)
- if packet then
- processPacket(packet)
- elseif reason and reason ~= "Timed out" then
- print("\n[!] WebSocket receive:\n " .. tostring(reason))
- closeSocket()
- end
- if loggedIn and #messageQueue > 0 and now() >= nextSend then
- local message = messageQueue[1]
- if sendChat(message) then
- table.remove(messageQueue, 1)
- print("[sent] " .. tostring(message))
- nextSend = now() + math.floor(SEND_DELAY * 1000)
- else
- print("[!] Send failed; keeping message queued.")
- closeSocket()
- end
- end
- if loggedIn and now() >= nextHeartbeat then
- if sendApplicationHeartbeat() then
- nextHeartbeat = now() + APP_HEARTBEAT * 1000
- else
- print("[!] Application heartbeat failed.")
- closeSocket()
- end
- end
- local silentFor = now() - lastPacketTime
- local maxSilence = math.max(60000, pingInterval + pingTimeout + 30000)
- if silentFor > maxSilence then
- print("\n[!] Connection dead. No packet for " .. math.floor(silentFor / 1000) .. "s. Reconnecting...")
- closeSocket()
- end
- else
- os.sleep(0.1)
- end
- end
- end
- print("[*] Starting bridge...")
- print("[*] Automatic reconnect enabled.")
- print("[*] Message queue enabled.\n")
- parallel.waitForAny(inputLoop, networkLoop)
- shuttingDown = true
- loggedIn, socketConnected = false, false
- if ws then
- pcall(function() ws.close() end)
- end
- ws = nil
- print("\n========================================")
- print(" BRIDGE STOPPED")
- print("========================================")
Advertisement