Advertisement
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 gpu = component.gpu
- local internet = component.internet
- local filesystem = require("filesystem")
- local computer = require("computer")
- 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 = {
- {name = "Libera.Chat", address = "irc.libera.chat", port = 6667},
- {name = "OFTC", address = "irc.oftc.net", port = 6667},
- {name = "EFnet", address = "irc.efnet.org", port = 6667},
- }
- local selectedServerIndex = 1
- local username = "User"
- local socket = nil
- local currentChannel = nil
- local messageBuffer = {}
- local maxBufferSize = 49
- local currentBufferIndex = 1
- local userList = {}
- local function saveServers()
- local file = io.open("/home/oc_irc_servers.txt", "w")
- if file then
- for _, server in ipairs(servers) do
- file:write(string.format("%s,%s,%d\n", server.name, server.address, server.port))
- end
- file:close()
- end
- end
- local function loadServers()
- local file = io.open("/home/oc_irc_servers.txt", "r")
- if file then
- servers = {}
- for line in file:lines() do
- local name, address, port = line:match("([^,]+),([^,]+),(%d+)")
- if name and address and port then
- table.insert(servers, {name = name, address = address, port = tonumber(port)})
- end
- end
- file:close()
- end
- end
- local function saveUsername()
- local file = io.open("/home/oc_irc_username.txt", "w")
- if file then file:write(username) file:close() end
- end
- local function loadUsername()
- local file = io.open("/home/oc_irc_username.txt", "r")
- if file then username = file:read("*all") file:close() end
- end
- loadServers()
- 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 (%s:%d)", i, server.name, server.address, server.port))
- else
- gpu.setForeground(0x00FF00)
- gpu.set(1, i, string.format("%d. %s (%s:%d)", i, server.name, server.address, server.port))
- gpu.setForeground(0xFFFFFF)
- end
- else
- gpu.set(1, i, string.format("%d. %s (%s:%d)", i, server.name, 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, "Press A to add a custom server, R to remove a server")
- gpu.set(1, #servers + 4, "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():gsub("\n", "")
- if socket then socket.write("NICK " .. newNick .. "\r\n") end
- username = newNick
- saveUsername()
- return newNick
- end
- local function addCustomServer()
- term.clear()
- print("Adding a custom server:")
- term.write("Enter server name: ")
- local name = term.read():gsub("\n", "")
- term.write("Enter server address: ")
- local address = term.read():gsub("\n", "")
- term.write("Enter server port: ")
- local portInput = term.read():gsub("\n", "")
- local port = tonumber(portInput)
- if not port or port < 1 or port > 65535 then
- print("Invalid port number. Please enter a number between 1 and 65535.")
- os.sleep(2)
- return
- end
- if name and address and port then
- table.insert(servers, {name = name, address = address, port = port})
- saveServers()
- print("Custom server added successfully!")
- else
- print("Invalid input. Server not added.")
- end
- os.sleep(2)
- end
- local function removeServer()
- term.clear()
- print("Removing a server:")
- for i, server in ipairs(servers) do
- print(string.format("%d. %s (%s:%d)", i, server.name, server.address, server.port))
- end
- term.write("Enter the number of the server to remove: ")
- local input = term.read():gsub("\n", "")
- local index = tonumber(input)
- if index and servers[index] then
- table.remove(servers, index)
- saveServers()
- print("Server removed successfully!")
- else
- print("Invalid input. No server removed.")
- end
- os.sleep(2)
- end
- local function readWithTimeout(socket, timeout)
- local buffer = ""
- local endTime = computer.uptime() + timeout
- while computer.uptime() < endTime do
- local chunk = socket.read(1024)
- if chunk then
- buffer = buffer .. chunk
- if buffer:find("\n") then return buffer end
- end
- os.sleep(0.05)
- end
- return buffer ~= "" and buffer or nil
- end
- local function addMessage(message)
- table.insert(messageBuffer, message)
- if #messageBuffer > maxBufferSize then table.remove(messageBuffer, 1) end
- currentBufferIndex = #messageBuffer
- end
- local function updateUserList(channel, users)
- if not userList[channel] then userList[channel] = {} end
- userList[channel] = {}
- for user in users:gmatch("%S+") do table.insert(userList[channel], user) end
- end
- local function drawModernUI()
- local width, height = gpu.getResolution()
- local userListWidth = 20
- local messageWidth = width - userListWidth - 1
- local inputHeight = 3
- local messageHeight = height - inputHeight - 1
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, width, height, " ")
- gpu.setBackground(0x1E1E1E)
- gpu.fill(messageWidth + 2, 1, userListWidth, height, " ")
- gpu.setForeground(0xFFFFFF)
- gpu.set(messageWidth + 2, 1, "Users")
- if currentChannel and userList[currentChannel] then
- for i, user in ipairs(userList[currentChannel]) do
- if i <= height - 2 then
- gpu.set(messageWidth + 2, i + 1, user:sub(1, userListWidth - 1))
- else break end
- end
- end
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, messageWidth, messageHeight, " ")
- gpu.setBackground(0x2D2D2D)
- gpu.fill(1, height - inputHeight + 1, width, inputHeight, " ")
- gpu.setForeground(0xFFFFFF)
- gpu.set(1, height - inputHeight + 1, "Input:")
- gpu.setBackground(0x4A4A4A)
- gpu.fill(1, messageHeight + 1, width, 1, "─")
- gpu.fill(messageWidth + 1, 1, 1, height, "│")
- gpu.set(messageWidth + 1, messageHeight + 1, "┴")
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- end
- local function wrapText(text, width)
- local lines = {}
- local line = ""
- for word in text:gmatch("%S+") do
- if #line + #word + 1 > width then
- table.insert(lines, line)
- line = word
- else
- if #line > 0 then line = line .. " " .. word
- else line = word end
- end
- end
- if #line > 0 then table.insert(lines, line) end
- return lines
- end
- local function displayBuffer()
- local width, height = gpu.getResolution()
- if getGPUTier() == 3 then
- local userListWidth = 20
- local messageWidth = width - userListWidth - 1
- local inputHeight = 3
- local messageHeight = height - inputHeight - 1
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, messageWidth, messageHeight, " ")
- local startIndex = math.max(1, #messageBuffer - messageHeight + 1)
- for i = 1, messageHeight do
- local message = messageBuffer[startIndex + i - 1]
- if message then
- gpu.set(1, i, message:sub(1, messageWidth))
- end
- end
- else
- term.clear()
- local startIndex = math.max(1, currentBufferIndex - maxBufferSize + 1)
- for i = startIndex, currentBufferIndex do print(messageBuffer[i] or "") end
- end
- 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 connect 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()
- 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
- local timeout = 30
- local start = computer.uptime()
- while computer.uptime() - start < timeout do
- local response = readWithTimeout(socket, 1)
- if response then
- for line in response:gmatch("[^\r\n]+") do
- addMessage(line)
- if line:find("433") then
- addMessage("Nickname is already in use. Please choose a different nickname.")
- displayBuffer()
- username = changeNickname()
- socket.write("NICK " .. username .. "\r\n")
- elseif line:find("001") then
- addMessage("Successfully connected to " .. server.address .. " as " .. username)
- connected = true
- break
- end
- end
- displayBuffer()
- end
- if connected then break end
- end
- if not connected then
- addMessage("Connection timed out or failed")
- displayBuffer()
- socket.close()
- 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 ping()
- if not socket then
- addMessage("Not connected to a server.")
- return
- end
- local start = computer.uptime()
- socket.write("PING :ocping\r\n")
- local timeout = computer.uptime() + 5
- while computer.uptime() < timeout do
- local response = readWithTimeout(socket, 0.1)
- if response and response:find("PONG :ocping") then
- local pingTime = (computer.uptime() - start) * 1000
- addMessage(string.format("Server ping: %.2f ms", pingTime))
- return
- end
- end
- addMessage("Ping timed out.")
- end
- local function showHelp()
- addMessage("Available commands:")
- addMessage(":helpoc - Show this help message")
- addMessage("connect #channel - Join a channel")
- addMessage("exit - Leave the current channel")
- addMessage("disconnect - Disconnect from the server")
- addMessage("ping - Ping the server and current channel")
- displayBuffer()
- end
- local function getTextInput(prompt, y)
- local width, _ = gpu.getResolution()
- local input = ""
- local cursorPos = 1
- local function redrawInput()
- gpu.fill(1, y, width, 1, " ")
- gpu.set(1, y, prompt .. input)
- gpu.set(#prompt + cursorPos, y, "_")
- end
- redrawInput()
- while true do
- local _, _, char, code = event.pull("key_down")
- if code == 28 then return input
- elseif code == 14 then
- if cursorPos > 1 then
- input = input:sub(1, cursorPos - 2) .. input:sub(cursorPos)
- cursorPos = cursorPos - 1
- end
- elseif code == 203 then cursorPos = math.max(1, cursorPos - 1)
- elseif code == 205 then cursorPos = math.min(#input + 1, cursorPos + 1)
- elseif char >= 32 and char <= 126 then
- input = input:sub(1, cursorPos - 1) .. string.char(char) .. input:sub(cursorPos)
- cursorPos = cursorPos + 1
- end
- redrawInput()
- end
- end
- local function handleUserInput(isCommand)
- local prefix = isCommand and "Command" or "Chat"
- local input
- if getGPUTier() == 3 then
- local width, height = gpu.getResolution()
- input = getTextInput(prefix .. "> ", height - 1)
- else
- term.write("\n" .. prefix .. "> ")
- input = term.read():gsub("\n", "")
- end
- if input then
- if isCommand then
- if input:match("^connect #") then
- currentChannel = input:match("^connect (.+)$")
- socket.write("JOIN " .. currentChannel .. "\r\n")
- elseif input == "disconnect" then
- return disconnectFromServer()
- elseif input == "exit" then
- return exitChannel()
- elseif input == ":helpoc" then
- showHelp()
- elseif input == "ping" then
- ping()
- else
- socket.write(input .. "\r\n")
- end
- else
- if currentChannel then
- socket.write("PRIVMSG " .. currentChannel .. " :" .. input .. "\r\n")
- addMessage("<" .. username .. "> " .. input)
- else
- addMessage("Not connected to a channel. Use 'connect #channel' to join one.")
- end
- end
- displayBuffer()
- end
- return false
- end
- local function handleServerCommunication()
- if getGPUTier() == 3 then drawModernUI() end
- addMessage("Connected to server. Press Ctrl for chat, Alt for commands.")
- addMessage("Use 'connect #channel' to join a channel, 'exit' to leave a channel, and 'disconnect' to disconnect from the server.")
- addMessage("Use 'ping' to ping the server and current channel.")
- addMessage("Use Up/Down arrows or PageUp/PageDown to scroll through messages.")
- displayBuffer()
- local buffer = ""
- while true do
- local chunk = socket.read(1024)
- if chunk then
- buffer = buffer .. chunk
- local lines = {}
- buffer = buffer:gsub("(.-)\r\n", function(line)
- table.insert(lines, line)
- return ""
- end)
- for _, line in ipairs(lines) do
- if line:find("^PING") then
- local pingId = line:match("^PING :(.+)")
- socket.write("PONG :" .. pingId .. "\r\n")
- elseif line:match("PRIVMSG .* :\001PING ") then
- local user = line:match("^:([^!]+)")
- local pingTime = line:match("PRIVMSG .* :\001PING (.+)\001")
- socket.write("NOTICE " .. user .. " :\001PING " .. pingTime .. "\001\r\n")
- elseif line:match("^:.+ 353 .+ = #%S+ :.*$") then
- local channel, users = line:match("^:.+ 353 .+ = (#%S+) :(.*)$")
- updateUserList(channel, users)
- if getGPUTier() == 3 then drawModernUI() end
- else
- addMessage(line)
- end
- end
- displayBuffer()
- end
- local eventName, _, char, code = event.pull(0.05)
- if eventName == "key_down" then
- if code == 29 then
- if handleUserInput(false) then return end
- elseif code == 56 then
- if handleUserInput(true) then return end
- elseif code == 200 then
- currentBufferIndex = math.max(1, currentBufferIndex - 1)
- displayBuffer()
- elseif code == 208 then
- currentBufferIndex = math.min(#messageBuffer, currentBufferIndex + 1)
- displayBuffer()
- elseif code == 201 then
- currentBufferIndex = math.max(1, currentBufferIndex - maxBufferSize)
- displayBuffer()
- elseif code == 209 then
- currentBufferIndex = math.min(#messageBuffer, currentBufferIndex + maxBufferSize)
- displayBuffer()
- end
- end
- end
- end
- local function updateMaxBufferSize()
- local _, height = gpu.getResolution()
- if getGPUTier() == 3 then
- maxBufferSize = height - 4
- else
- maxBufferSize = 49
- end
- end
- local function main()
- while true do
- drawServerList()
- local eventName, _, char, code = event.pull("key_down")
- if char == 120 or char == 88 then
- print("Exiting OC-IRC...")
- os.exit()
- elseif code == 200 then
- selectedServerIndex = math.max(1, selectedServerIndex - 1)
- elseif code == 208 then
- selectedServerIndex = math.min(#servers, selectedServerIndex + 1)
- elseif code == 203 then
- selectedServerIndex = math.max(1, selectedServerIndex - 1)
- elseif code == 205 then
- selectedServerIndex = math.min(#servers, selectedServerIndex + 1)
- elseif char == 110 or char == 78 then
- username = changeNickname()
- elseif char == 97 or char == 65 then
- addCustomServer()
- elseif char == 114 or char == 82 then
- removeServer()
- elseif code == 28 then
- term.clear()
- socket = connectToServer(servers[selectedServerIndex])
- if not socket then
- addMessage("Connection failed. Press any key to return to server list.")
- displayBuffer()
- event.pull("key_down")
- else
- if isColorSupported then
- gpu.setResolution(gpu.maxResolution())
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- end
- updateMaxBufferSize()
- handleServerCommunication()
- end
- end
- end
- end
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement