Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local term = require("term")
- local event = require("event")
- local keyboard = require("keyboard")
- local filesystem = require("filesystem")
- local computer = require("computer")
- if not component.isAvailable("internet") then
- io.stderr:write("This program requires an Internet Card to function.\n")
- return
- end
- local gpu = component.gpu
- local internet = component.internet
- local function getGPUTier()
- local w, h = gpu.maxResolution()
- if w == 50 and h == 16 then return 1
- elseif w == 80 and h == 25 then return 2
- else return 3 end
- end
- local isColorSupported = false
- if gpu and gpu.maxDepth then
- isColorSupported = gpu.maxDepth() > 1
- end
- local servers = {
- {address = "atw.hu.eu.dal.net", port = 6667},
- {address = "bifrost.ca.us.dal.net", port = 6667},
- {address = "bitcoin.uk.eu.dal.net", port = 6667},
- {address = "choopa.nj.us.dal.net", port = 6667},
- {address = "halcyon.il.us.dal.net", port = 6667},
- {address = "istana.il.us.dal.net", port = 6667},
- {address = "lair.nl.eu.dal.net", port = 6667},
- {address = "lion.tx.us.dal.net", port = 6667},
- {address = "punch.wa.us.dal.net", port = 6667},
- {address = "sakura.jp.as.dal.net", port = 6667},
- {address = "serenity.fl.us.dal.net", port = 6667},
- {address = "nonstop.ix.me.dal.net", port = 6667},
- }
- local selectedServerIndex = 1
- local username = "User"
- local socket = nil
- local currentChannel = nil
- local messageBuffer = {}
- local currentBufferIndex = 1
- local function saveUsername()
- local file = io.open("/home/dalnet_username.txt", "w")
- if file then
- file:write(username)
- file:close()
- end
- end
- local function loadUsername()
- local file = io.open("/home/dalnet_username.txt", "r")
- if file then
- username = file:read("*all"):gsub("%s+", "")
- file:close()
- end
- end
- loadUsername()
- local function drawLogo()
- if getGPUTier() == 1 then return end
- local logo = {
- " ________ ________ ________ ________ ___ ________ _______ _________ ",
- "|\\ __ \\|\\ ____\\ |\\ ___ \\|\\ __ \\|\\ \\ |\\ ___ \\|\\ ___ \\|\\___ ___\\ ",
- "\\ \\ \\|\\ \\ \\ \\___| \\ \\ \\_|\\ \\ \\ \\|\\ \\ \\ \\ \\ \\ \\\\ \\ \\ \\ \\_/__\\|___ \\ \\_| ",
- " \\ \\ \\\\\\ \\ \\ \\ \\ \\ \\ \\\\ \\ \\ __ \\ \\ \\ \\ \\ \\\\ \\ \\ \\ \\_|/__ \\ \\ \\ ",
- " \\ \\ \\\\\\ \\ \\ \\____ \\ \\ \\_\\\\ \\ \\ \\ \\ \\ \\ \\____\\ \\ \\\\ \\ \\ \\ \\_|\\ \\ \\ \\ \\ ",
- " \\ \\_______\\ \\_______\\ \\ \\_______\\ \\__\\ \\__\\ \\_______\\ \\__\\\\ \\__\\ \\_______\\ \\ \\__\\",
- " \\|_______|\\|_______| \\|_______|\\|__|\\|__|\\|_______|\\|__| \\|__|\\|_______| \\|__|"
- }
- local width, height = gpu.getResolution()
- local startY = height - #logo - 1
- for i, line in ipairs(logo) do
- gpu.set(1, startY + i, line)
- end
- end
- local function drawServerList()
- local oldForeground = gpu.getForeground()
- local oldBackground = gpu.getBackground()
- gpu.setBackground(0x000000)
- term.clear()
- for i, server in ipairs(servers) do
- if i == selectedServerIndex then
- if getGPUTier() == 1 then
- gpu.set(1, i, "-> " .. string.format("%d. %s:%d", i, server.address, server.port))
- else
- gpu.setForeground(0x00FF00)
- gpu.set(1, i, string.format("%d. %s:%d", i, server.address, server.port))
- gpu.setForeground(0xFFFFFF)
- end
- else
- gpu.set(1, i, string.format("%d. %s:%d", i, server.address, server.port))
- end
- end
- gpu.setForeground(0xFFFFFF)
- gpu.set(1, #servers + 2, "Press N to change username. Current username: " .. username)
- gpu.set(1, #servers + 3, "Use arrow keys to navigate, Enter to connect, X to exit")
- drawLogo()
- gpu.setForeground(oldForeground)
- gpu.setBackground(oldBackground)
- end
- local function changeNickname()
- term.write("\nEnter new nickname: ")
- local newNick = term.read()
- if not newNick then return username end
- newNick = newNick:gsub("[\r\n%s]", "")
- if socket and #newNick > 0 then
- socket.write("NICK " .. newNick .. "\r\n")
- end
- if #newNick > 0 then
- username = newNick
- saveUsername()
- end
- return username
- end
- -- Strips IRC color formatting codes, bold, underline, and annoying ASCII control characters
- local function cleanIRCFormatting(str)
- if not str then return "" end
- -- 1. Strip color codes: \x03 followed by up to 2 digits, optionally a comma and up to 2 more digits
- str = str:gsub("\x03%d?%d?,?%d?%d?", "")
- -- 2. Strip other formatting bytes: \x02 (bold), \x1F (underline), \x16 (reverse video), \x0F (reset)
- str = str:gsub("[\x02\x1F\x16\x0F]", "")
- -- 3. Strip remaining structural low ASCII control chars (except standard space)
- str = str:gsub("[\x01-\x1F\x7F]", "")
- return str
- end
- local function addMessage(message)
- table.insert(messageBuffer, cleanIRCFormatting(message))
- if #messageBuffer > 150 then
- table.remove(messageBuffer, 1)
- end
- currentBufferIndex = #messageBuffer
- end
- local function displayBuffer()
- term.clear()
- local w, h = gpu.getResolution()
- local viewHeight = h - 2
- local startIndex = math.max(1, currentBufferIndex - viewHeight + 1)
- local endIndex = math.min(#messageBuffer, startIndex + viewHeight - 1)
- local row = 1
- for i = startIndex, endIndex do
- gpu.set(1, row, messageBuffer[i] or "")
- row = row + 1
- end
- local status = string.format("[Channel: %s] [Ctrl: Chat | Alt: Cmd] ", currentChannel or "None")
- gpu.set(1, h - 1, string.rep("-", w))
- gpu.set(1, h, status)
- end
- local networkBuffer = ""
- local function parseIncomingNetwork()
- if not socket then return nil end
- local success, chunk = pcall(socket.read, 2048)
- if not success or not chunk or #chunk == 0 then return nil end
- networkBuffer = networkBuffer .. chunk
- local lines = {}
- while networkBuffer:find("\r\n") do
- local line, rest = networkBuffer:match("^([^\r\n]*)\r\n(.*)$")
- table.insert(lines, line)
- networkBuffer = rest
- end
- return lines
- end
- local function connectToServer(server)
- term.clear()
- addMessage("Connecting to " .. server.address .. " as " .. username)
- displayBuffer()
- socket = internet.connect(server.address, server.port)
- if not socket then
- addMessage("Failed to initialize connection to " .. server.address)
- displayBuffer()
- return nil
- end
- local timeout = 10
- local start = computer.uptime()
- while not socket.finishConnect() do
- if computer.uptime() - start > timeout then
- addMessage("Connection timed out.")
- displayBuffer()
- socket.close()
- socket = nil
- return nil
- end
- os.sleep(0.1)
- end
- socket.write("NICK " .. username .. "\r\n")
- socket.write("USER " .. username .. " 0 * :" .. username .. "\r\n")
- local connected = false
- timeout = 20
- start = computer.uptime()
- while computer.uptime() - start < timeout do
- local lines = parseIncomingNetwork()
- if lines then
- for _, line in ipairs(lines) do
- addMessage(line)
- if line:find(" 433 ") then
- addMessage("Nickname is already in use!")
- displayBuffer()
- username = changeNickname()
- socket.write("NICK " .. username .. "\r\n")
- elseif line:find(" 001 ") then
- addMessage("Successfully connected to " .. server.address)
- connected = true
- break
- end
- end
- displayBuffer()
- end
- if connected then break end
- os.sleep(0.1)
- end
- if not connected then
- addMessage("Connection failed/Rejected.")
- displayBuffer()
- if socket then socket.close() end
- socket = nil
- return nil
- end
- return socket
- end
- local function disconnectFromServer()
- if socket then
- socket.write("QUIT :Disconnecting\r\n")
- socket.close()
- socket = nil
- currentChannel = nil
- addMessage("Disconnected from server.")
- displayBuffer()
- return true
- end
- return false
- end
- local function exitChannel()
- if currentChannel then
- socket.write("PART " .. currentChannel .. " :Leaving\r\n")
- addMessage("Left channel: " .. currentChannel)
- currentChannel = nil
- displayBuffer()
- return true
- end
- return false
- end
- local function showHelp()
- addMessage("Available commands:")
- addMessage(":helpoc - Show this help message")
- addMessage("join #channel - Join an IRC channel")
- addMessage("exit - Leave the current channel")
- addMessage("disconnect - Disconnect from the server")
- displayBuffer()
- end
- local function handleUserInput(isCommand)
- local w, h = gpu.getResolution()
- gpu.set(1, h, string.rep(" ", w))
- gpu.set(1, h, isCommand and "Command> " or "Chat> ")
- local input = term.read()
- if input then
- input = input:gsub("[\r\n]", "")
- if #input == 0 then displayBuffer() return false end
- if isCommand then
- local joinMatch = input:match("^join%s+(#%S+)") or input:match("^connect%s+(#%S+)")
- if joinMatch then
- currentChannel = joinMatch
- socket.write("JOIN " .. currentChannel .. "\r\n")
- elseif input == "disconnect" then
- return disconnectFromServer()
- elseif input == "exit" then
- return exitChannel()
- elseif input == ":helpoc" then
- showHelp()
- else
- socket.write(input .. "\r\n")
- end
- else
- if currentChannel then
- socket.write("PRIVMSG " .. currentChannel .. " :" .. input .. "\r\n")
- addMessage("<" .. username .. "> " .. input)
- else
- addMessage("Not in a channel. Tap Alt and type: join #channel")
- end
- end
- displayBuffer()
- end
- return false
- end
- local function handleServerCommunication()
- addMessage("Connected! Tap L/R Ctrl to type messages, Tap L/R Alt for commands.")
- displayBuffer()
- while socket do
- local lines = parseIncomingNetwork()
- if lines then
- for _, line in ipairs(lines) do
- if line:find("^PING") then
- local pingId = line:match("PING :?(.*)")
- socket.write("PONG :" .. pingId .. "\r\n")
- else
- local nick, target, msg = line:match("^:(.-)!.- PRIVMSG%s+(%S+)%s+:(.*)$")
- if nick and msg then
- addMessage("[" .. target .. "] <" .. nick .. "> " .. msg)
- else
- addMessage(line)
- end
- end
- end
- displayBuffer()
- end
- local eventName, _, char, code = event.pull(0.05)
- if eventName == "key_down" then
- if code == keyboard.keys.lcontrol or code == keyboard.keys.rcontrol then
- if handleUserInput(false) then return end
- elseif code == keyboard.keys.lmenu or code == keyboard.keys.rmenu then
- if handleUserInput(true) then return end
- elseif code == keyboard.keys.up then
- currentBufferIndex = math.max(1, currentBufferIndex - 1)
- displayBuffer()
- elseif code == keyboard.keys.down then
- currentBufferIndex = math.min(#messageBuffer, currentBufferIndex + 1)
- displayBuffer()
- end
- end
- end
- end
- local function main()
- while true do
- drawServerList()
- local eventName, _, char, code = event.pull("key_down")
- if char == 120 or char == 88 then
- term.clear()
- print("Exiting program...")
- break
- elseif code == keyboard.keys.up or code == keyboard.keys.left then
- selectedServerIndex = math.max(1, selectedServerIndex - 1)
- elseif code == keyboard.keys.down or code == keyboard.keys.right then
- selectedServerIndex = math.min(#servers, selectedServerIndex + 1)
- elseif char == 110 or char == 78 then
- username = changeNickname()
- elseif code == keyboard.keys.enter then
- term.clear()
- if connectToServer(servers[selectedServerIndex]) then
- if isColorSupported then
- gpu.setResolution(gpu.maxResolution())
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- end
- handleServerCommunication()
- else
- addMessage("Press any key to return to server list.")
- displayBuffer()
- event.pull("key_down")
- end
- end
- end
- end
- main()
Advertisement
Add Comment
Please, Sign In to add comment