Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local term = require("term")
- local unicode = require("unicode")
- local serialization = require("serialization")
- local filesystem = require("filesystem")
- local keyboard = require("keyboard")
- local gpu = component.gpu
- local computer = require("computer")
- local internet = component.internet
- if not internet then error("No internet card found. Please install an Internet Card.") end
- local CONFIG_FILE = "/home/irc_config.cfg"
- local default_config = {
- server = "irc.libera.chat",
- port = 6667,
- nickname = "OCBot",
- username = "OCUser",
- realname = "OpenComputers IRC User",
- channel = "#opencomputers"
- }
- local function loadConfig()
- if filesystem.exists(CONFIG_FILE) then
- local file = io.open(CONFIG_FILE, "r")
- if file then
- local content = file:read("*all")
- file:close()
- return serialization.unserialize(content) or default_config
- end
- end
- return default_config
- end
- local function saveConfig(config)
- local file = io.open(CONFIG_FILE, "w")
- if file then
- file:write(serialization.serialize(config))
- file:close()
- print("Configuration saved successfully.")
- else
- print("Error: Unable to save configuration.")
- end
- end
- local function configMenu(config)
- while true do
- term.clear()
- print("IRC Client Configuration")
- print("------------------------")
- print("1. Server: " .. config.server)
- print("2. Port: " .. config.port)
- print("3. Nickname: " .. config.nickname)
- print("4. Username: " .. config.username)
- print("5. Real Name: " .. config.realname)
- print("6. Channel: " .. config.channel)
- print("7. Save and Exit")
- print("8. Exit without saving")
- io.write("Enter your choice (1-8): ")
- local choice = io.read()
- if choice == "1" then
- io.write("Enter new server: ")
- config.server = io.read()
- elseif choice == "2" then
- io.write("Enter new port: ")
- config.port = tonumber(io.read()) or config.port
- elseif choice == "3" then
- io.write("Enter new nickname: ")
- config.nickname = io.read()
- elseif choice == "4" then
- io.write("Enter new username: ")
- config.username = io.read()
- elseif choice == "5" then
- io.write("Enter new real name: ")
- config.realname = io.read()
- elseif choice == "6" then
- io.write("Enter new channel: ")
- config.channel = io.read()
- elseif choice == "7" then
- saveConfig(config)
- break
- elseif choice == "8" then
- break
- end
- end
- end
- -- UI Helper Functions
- local function drawBox(x, y, width, height, color)
- local oldColor = gpu.getBackground()
- gpu.setBackground(color)
- gpu.fill(x, y, width, height, " ")
- gpu.setBackground(oldColor)
- end
- local function drawText(x, y, text, fgColor, bgColor)
- local oldFg, oldBg = gpu.getForeground(), gpu.getBackground()
- gpu.setForeground(fgColor)
- gpu.setBackground(bgColor)
- gpu.set(x, y, text)
- gpu.setForeground(oldFg)
- gpu.setBackground(oldBg)
- end
- -- UI Components
- local function createChatArea(x, y, width, height)
- local messages = {}
- local scrollPosition = 0
- local function addMessage(msg)
- table.insert(messages, msg)
- if #messages > height then
- table.remove(messages, 1)
- end
- scrollPosition = math.max(0, #messages - height)
- end
- local function draw()
- drawBox(x, y, width, height, 0x000000)
- for i = 1, height do
- local msg = messages[i + scrollPosition]
- if msg then
- drawText(x, y + i - 1, unicode.sub(msg, 1, width), 0xFFFFFF, 0x000000)
- end
- end
- end
- return {
- addMessage = addMessage,
- draw = draw
- }
- end
- local function createUserList(x, y, width, height)
- local users = {}
- local function updateUsers(newUsers)
- users = {}
- for user in newUsers do
- table.insert(users, user)
- end
- end
- local function draw()
- drawBox(x, y, width, height, 0x0000FF)
- drawText(x, y, "Users", 0xFFFFFF, 0x0000FF)
- for i, user in ipairs(users) do
- if i > height - 1 then break end
- drawText(x, y + i, user, 0xFFFFFF, 0x0000FF)
- end
- end
- return {
- updateUsers = updateUsers,
- draw = draw
- }
- end
- local function createInputArea(x, y, width)
- local input = ""
- local cursorPos = 0
- local function handleKey(char, code)
- if char > 0 then
- input = unicode.sub(input, 1, cursorPos) .. unicode.char(char) .. unicode.sub(input, cursorPos + 1)
- cursorPos = cursorPos + 1
- elseif code == keyboard.keys.back and cursorPos > 0 then
- input = unicode.sub(input, 1, cursorPos - 1) .. unicode.sub(input, cursorPos + 1)
- cursorPos = cursorPos - 1
- elseif code == keyboard.keys.left and cursorPos > 0 then
- cursorPos = cursorPos - 1
- elseif code == keyboard.keys.right and cursorPos < unicode.len(input) then
- cursorPos = cursorPos + 1
- end
- end
- local function draw()
- drawBox(x, y, width, 1, 0x000000)
- drawText(x, y, unicode.sub(input, 1, width), 0xFFFFFF, 0x000000)
- gpu.set(x + cursorPos, y, "_")
- end
- local function getText()
- local text = input
- input = ""
- cursorPos = 0
- return text
- end
- return {
- handleKey = handleKey,
- draw = draw,
- getText = getText
- }
- end
- local function runClient(config)
- local width, height = gpu.getResolution()
- local chatArea = createChatArea(1, 1, width - 20, height - 2)
- local userList = createUserList(width - 19, 1, 20, height - 2)
- local inputArea = createInputArea(1, height, width)
- local socket = internet.connect(config.server, config.port)
- if not socket then
- error("Failed to connect to IRC server")
- end
- local function send(msg)
- socket.write(msg .. "\r\n")
- chatArea.addMessage("Sent: " .. msg)
- end
- local registered = false
- local nick = config.nickname
- local username = config.username
- local realname = config.realname
- local nickAttempts = 0
- local connectionAttempts = 0
- local MAX_CONNECTION_ATTEMPTS = 3
- local function register()
- send("USER " .. username .. " 0 * :" .. realname)
- send("NICK " .. nick)
- end
- local function handleNickError()
- nickAttempts = nickAttempts + 1
- nick = config.nickname .. nickAttempts
- chatArea.addMessage("Nickname in use or connection incomplete, trying " .. nick)
- send("NICK " .. nick)
- end
- local function connect()
- while connectionAttempts < MAX_CONNECTION_ATTEMPTS do
- connectionAttempts = connectionAttempts + 1
- chatArea.addMessage("Connection attempt " .. connectionAttempts)
- register()
- -- Wait for registration confirmation or error
- local timeout = computer.uptime() + 10 -- 10 second timeout
- while computer.uptime() < timeout do
- local data = socket.read()
- if data then
- for line in data:gmatch("[^\r\n]+") do
- chatArea.addMessage("Received: " .. line)
- if line:match("^:.*001") then
- registered = true
- return true
- elseif line:match("451.*finish connecting") then
- handleNickError()
- os.sleep(2) -- Add a small delay before retrying
- break
- end
- end
- end
- os.sleep(0.1)
- end
- if registered then
- return true
- end
- chatArea.addMessage("Connection attempt failed, retrying...")
- os.sleep(5) -- Wait 5 seconds before next attempt
- end
- return false
- end
- if not connect() then
- chatArea.addMessage("Failed to connect after " .. MAX_CONNECTION_ATTEMPTS .. " attempts")
- return
- end
- local function handleServerMessage(prefix, command, params)
- if not params then
- chatArea.addMessage("Warning: Received message with nil params")
- return
- end
- if command == "001" then
- registered = true
- chatArea.addMessage("Successfully registered as " .. nick)
- send("JOIN " .. config.channel)
- elseif command == "433" then
- if not registered then
- handleNickError()
- end
- elseif command == "353" then
- if params[#params] then
- local users = params[#params]:gmatch("%S+")
- userList.updateUsers(users)
- else
- chatArea.addMessage("Warning: Invalid user list received")
- end
- elseif command == "PRIVMSG" then
- if #params >= 2 then
- local target, message = params[1], params[2]
- local sender = prefix:match("^([^!]+)")
- if target == config.channel then
- chatArea.addMessage("<" .. sender .. "> " .. message)
- else
- chatArea.addMessage("*" .. sender .. "* " .. message)
- end
- else
- chatArea.addMessage("Warning: Invalid PRIVMSG format")
- end
- elseif command == "JOIN" then
- local joiner = prefix:match("^([^!]+)")
- chatArea.addMessage("* " .. joiner .. " has joined " .. (params[1] or "unknown channel"))
- elseif command == "PART" then
- local leaver = prefix:match("^([^!]+)")
- chatArea.addMessage("* " .. leaver .. " has left " .. (params[1] or "unknown channel"))
- elseif command == "QUIT" then
- local quitter = prefix:match("^([^!]+)")
- chatArea.addMessage("* " .. quitter .. " has quit")
- end
- end
- local running = true
- local lastDrawTime = 0
- local function processIncomingMessages()
- local data = socket.read()
- if data then
- for line in data:gmatch("[^\r\n]+") do
- chatArea.addMessage("Received: " .. line)
- if line:match("^PING :(.+)") then
- local server = line:match("^PING :(.+)")
- send("PONG :" .. server)
- else
- local prefix, command, params = line:match("^(:(%S+) )?(%S+)(.*)")
- if prefix then prefix = prefix:sub(2) end
- params = params and params:gsub("^%s*", "") or ""
- local paramTable = {}
- for param in params:gmatch("([^%s:]+)") do
- table.insert(paramTable, param)
- end
- if params:match(" :") then
- table.insert(paramTable, params:match(" :(.*)$"))
- end
- handleServerMessage(prefix, command, paramTable)
- end
- end
- end
- end
- while running do
- local currentTime = computer.uptime()
- if currentTime - lastDrawTime >= 0.1 then -- Redraw every 0.1 seconds
- chatArea.draw()
- userList.draw()
- inputArea.draw()
- lastDrawTime = currentTime
- end
- local eventType, _, char, code = event.pull(0.05)
- if eventType == "key_down" then
- if char == 13 then -- Enter key
- local message = inputArea.getText()
- if message ~= "" then
- send("PRIVMSG " .. config.channel .. " :" .. message)
- chatArea.addMessage("<" .. nick .. "> " .. message)
- end
- else
- inputArea.handleKey(char, code)
- end
- elseif eventType == "interrupted" then
- running = false
- end
- processIncomingMessages()
- end
- socket.close()
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- term.clear()
- print("Disconnected from IRC server.")
- end
- local logo = [[
- U ___ u ____ ____ ____
- \/"_ \/U /"___| ___ U | _"\ u U /"___|
- | | | |\| | u U u |_"_| \| |_) |/ \| | u
- .-,_| |_| | | |/__ /___\ | | | _ < | |/__
- \_)-\___/ \____|__"__|U/| |\u |_| \_\ \____|
- \\ _// \\ .-,_|___|_,-.// \\_ _// \\
- (__) (__)(__) \_)-' '-(_/(__) (__)(__)(__)
- ]]
- local function main()
- term.clear()
- print(logo)
- local config = loadConfig()
- while true do
- print("\nIRC Client Menu")
- print("1. Start IRC Client")
- print("2. Edit Configuration")
- print("3. Exit")
- io.write("Enter your choice (1-3): ")
- local choice = io.read()
- if choice == "1" then
- print("Connecting to " .. config.server .. "...")
- runClient(config)
- elseif choice == "2" then
- configMenu(config)
- elseif choice == "3" then
- break
- end
- end
- print("Goodbye!")
- end
- local status, err = pcall(main)
- if not status then
- print("An error occurred:")
- print(err)
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement