nonogamer9

OC-DALNET: DALNET For OpenComputers

Jul 24th, 2024 (edited)
590
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.18 KB | Software | 0 0
  1. local component = require("component")
  2. local term = require("term")
  3. local event = require("event")
  4. local keyboard = require("keyboard")
  5. local filesystem = require("filesystem")
  6. local computer = require("computer")
  7.  
  8. if not component.isAvailable("internet") then
  9.   io.stderr:write("This program requires an Internet Card to function.\n")
  10.   return
  11. end
  12.  
  13. local gpu = component.gpu
  14. local internet = component.internet
  15.  
  16. local function getGPUTier()
  17.   local w, h = gpu.maxResolution()
  18.   if w == 50 and h == 16 then return 1
  19.   elseif w == 80 and h == 25 then return 2
  20.   else return 3 end
  21. end
  22.  
  23. local isColorSupported = false
  24. if gpu and gpu.maxDepth then
  25.     isColorSupported = gpu.maxDepth() > 1
  26. end
  27.  
  28. local servers = {
  29.   {address = "atw.hu.eu.dal.net", port = 6667},
  30.   {address = "bifrost.ca.us.dal.net", port = 6667},
  31.   {address = "bitcoin.uk.eu.dal.net", port = 6667},
  32.   {address = "choopa.nj.us.dal.net", port = 6667},
  33.   {address = "halcyon.il.us.dal.net", port = 6667},
  34.   {address = "istana.il.us.dal.net", port = 6667},
  35.   {address = "lair.nl.eu.dal.net", port = 6667},
  36.   {address = "lion.tx.us.dal.net", port = 6667},
  37.   {address = "punch.wa.us.dal.net", port = 6667},
  38.   {address = "sakura.jp.as.dal.net", port = 6667},
  39.   {address = "serenity.fl.us.dal.net", port = 6667},
  40.   {address = "nonstop.ix.me.dal.net", port = 6667},
  41. }
  42.  
  43. local selectedServerIndex = 1
  44. local username = "User"
  45. local socket = nil
  46. local currentChannel = nil
  47.  
  48. local messageBuffer = {}
  49. local currentBufferIndex = 1
  50.  
  51. local function saveUsername()
  52.   local file = io.open("/home/dalnet_username.txt", "w")
  53.   if file then
  54.     file:write(username)
  55.     file:close()
  56.   end
  57. end
  58.  
  59. local function loadUsername()
  60.   local file = io.open("/home/dalnet_username.txt", "r")
  61.   if file then
  62.     username = file:read("*all"):gsub("%s+", "")
  63.     file:close()
  64.   end
  65. end
  66.  
  67. loadUsername()
  68.  
  69. local function drawLogo()
  70.   if getGPUTier() == 1 then return end
  71.  
  72.   local logo = {
  73.     " ________  ________          ________  ________  ___       ________   _______  _________   ",
  74.     "|\\   __  \\|\\   ____\\        |\\   ___ \\|\\   __  \\|\\  \\     |\\   ___  \\|\\  ___ \\|\\___   ___\\ ",
  75.     "\\ \\  \\|\\  \\ \\  \\___|        \\ \\  \\_|\\ \\ \\  \\|\\  \\ \\  \\    \\ \\  \\\\ \\  \\ \\  \\_/__\\|___ \\  \\_| ",
  76.     " \\ \\  \\\\\\  \\ \\  \\            \\ \\  \\ \\\\ \\ \\  __  \\ \\  \\    \\ \\  \\\\ \\  \\ \\  \\_|/__    \\ \\  \\  ",
  77.     "  \\ \\  \\\\\\  \\ \\  \\____        \\ \\  \\_\\\\ \\ \\  \\ \\  \\ \\  \\____\\ \\  \\\\ \\  \\ \\  \\_|\\ \\   \\ \\  \\ ",
  78.     "   \\ \\_______\\ \\_______\\        \\ \\_______\\ \\__\\ \\__\\ \\_______\\ \\__\\\\ \\__\\ \\_______\\   \\ \\__\\",
  79.     "    \\|_______|\\|_______|         \\|_______|\\|__|\\|__|\\|_______|\\|__| \\|__|\\|_______|    \\|__|"
  80.   }
  81.  
  82.   local width, height = gpu.getResolution()
  83.   local startY = height - #logo - 1
  84.  
  85.   for i, line in ipairs(logo) do
  86.     gpu.set(1, startY + i, line)
  87.   end
  88. end
  89.  
  90. local function drawServerList()
  91.   local oldForeground = gpu.getForeground()
  92.   local oldBackground = gpu.getBackground()
  93.  
  94.   gpu.setBackground(0x000000)
  95.   term.clear()
  96.  
  97.   for i, server in ipairs(servers) do
  98.     if i == selectedServerIndex then
  99.       if getGPUTier() == 1 then
  100.         gpu.set(1, i, "-> " .. string.format("%d. %s:%d", i, server.address, server.port))
  101.       else
  102.         gpu.setForeground(0x00FF00)
  103.         gpu.set(1, i, string.format("%d. %s:%d", i, server.address, server.port))
  104.         gpu.setForeground(0xFFFFFF)
  105.       end
  106.     else
  107.       gpu.set(1, i, string.format("%d. %s:%d", i, server.address, server.port))
  108.     end
  109.   end
  110.  
  111.   gpu.setForeground(0xFFFFFF)
  112.   gpu.set(1, #servers + 2, "Press N to change username. Current username: " .. username)
  113.   gpu.set(1, #servers + 3, "Use arrow keys to navigate, Enter to connect, X to exit")
  114.  
  115.   drawLogo()
  116.  
  117.   gpu.setForeground(oldForeground)
  118.   gpu.setBackground(oldBackground)
  119. end
  120.  
  121. local function changeNickname()
  122.   term.write("\nEnter new nickname: ")
  123.   local newNick = term.read()
  124.   if not newNick then return username end
  125.   newNick = newNick:gsub("[\r\n%s]", "")
  126.   if socket and #newNick > 0 then
  127.     socket.write("NICK " .. newNick .. "\r\n")
  128.   end
  129.   if #newNick > 0 then
  130.     username = newNick
  131.     saveUsername()
  132.   end
  133.   return username
  134. end
  135.  
  136. -- Strips IRC color formatting codes, bold, underline, and annoying ASCII control characters
  137. local function cleanIRCFormatting(str)
  138.   if not str then return "" end
  139.   -- 1. Strip color codes: \x03 followed by up to 2 digits, optionally a comma and up to 2 more digits
  140.   str = str:gsub("\x03%d?%d?,?%d?%d?", "")
  141.   -- 2. Strip other formatting bytes: \x02 (bold), \x1F (underline), \x16 (reverse video), \x0F (reset)
  142.   str = str:gsub("[\x02\x1F\x16\x0F]", "")
  143.   -- 3. Strip remaining structural low ASCII control chars (except standard space)
  144.   str = str:gsub("[\x01-\x1F\x7F]", "")
  145.   return str
  146. end
  147.  
  148. local function addMessage(message)
  149.   table.insert(messageBuffer, cleanIRCFormatting(message))
  150.   if #messageBuffer > 150 then
  151.     table.remove(messageBuffer, 1)
  152.   end
  153.   currentBufferIndex = #messageBuffer
  154. end
  155.  
  156. local function displayBuffer()
  157.   term.clear()
  158.   local w, h = gpu.getResolution()
  159.   local viewHeight = h - 2
  160.  
  161.   local startIndex = math.max(1, currentBufferIndex - viewHeight + 1)
  162.   local endIndex = math.min(#messageBuffer, startIndex + viewHeight - 1)
  163.  
  164.   local row = 1
  165.   for i = startIndex, endIndex do
  166.     gpu.set(1, row, messageBuffer[i] or "")
  167.     row = row + 1
  168.   end
  169.  
  170.   local status = string.format("[Channel: %s] [Ctrl: Chat | Alt: Cmd] ", currentChannel or "None")
  171.   gpu.set(1, h - 1, string.rep("-", w))
  172.   gpu.set(1, h, status)
  173. end
  174.  
  175. local networkBuffer = ""
  176. local function parseIncomingNetwork()
  177.   if not socket then return nil end
  178.  
  179.   local success, chunk = pcall(socket.read, 2048)
  180.   if not success or not chunk or #chunk == 0 then return nil end
  181.  
  182.   networkBuffer = networkBuffer .. chunk
  183.   local lines = {}
  184.   while networkBuffer:find("\r\n") do
  185.     local line, rest = networkBuffer:match("^([^\r\n]*)\r\n(.*)$")
  186.     table.insert(lines, line)
  187.     networkBuffer = rest
  188.   end
  189.   return lines
  190. end
  191.  
  192. local function connectToServer(server)
  193.   term.clear()
  194.   addMessage("Connecting to " .. server.address .. " as " .. username)
  195.   displayBuffer()
  196.  
  197.   socket = internet.connect(server.address, server.port)
  198.   if not socket then
  199.     addMessage("Failed to initialize connection to " .. server.address)
  200.     displayBuffer()
  201.     return nil
  202.   end
  203.  
  204.   local timeout = 10
  205.   local start = computer.uptime()
  206.   while not socket.finishConnect() do
  207.     if computer.uptime() - start > timeout then
  208.       addMessage("Connection timed out.")
  209.       displayBuffer()
  210.       socket.close()
  211.       socket = nil
  212.       return nil
  213.     end
  214.     os.sleep(0.1)
  215.   end
  216.  
  217.   socket.write("NICK " .. username .. "\r\n")
  218.   socket.write("USER " .. username .. " 0 * :" .. username .. "\r\n")
  219.  
  220.   local connected = false
  221.   timeout = 20
  222.   start = computer.uptime()
  223.  
  224.   while computer.uptime() - start < timeout do
  225.     local lines = parseIncomingNetwork()
  226.     if lines then
  227.       for _, line in ipairs(lines) do
  228.         addMessage(line)
  229.         if line:find(" 433 ") then
  230.           addMessage("Nickname is already in use!")
  231.           displayBuffer()
  232.           username = changeNickname()
  233.           socket.write("NICK " .. username .. "\r\n")
  234.         elseif line:find(" 001 ") then
  235.           addMessage("Successfully connected to " .. server.address)
  236.           connected = true
  237.           break
  238.         end
  239.       end
  240.       displayBuffer()
  241.     end
  242.     if connected then break end
  243.     os.sleep(0.1)
  244.   end
  245.  
  246.   if not connected then
  247.     addMessage("Connection failed/Rejected.")
  248.     displayBuffer()
  249.     if socket then socket.close() end
  250.     socket = nil
  251.     return nil
  252.   end
  253.  
  254.   return socket
  255. end
  256.  
  257. local function disconnectFromServer()
  258.   if socket then
  259.     socket.write("QUIT :Disconnecting\r\n")
  260.     socket.close()
  261.     socket = nil
  262.     currentChannel = nil
  263.     addMessage("Disconnected from server.")
  264.     displayBuffer()
  265.     return true
  266.   end
  267.   return false
  268. end
  269.  
  270. local function exitChannel()
  271.   if currentChannel then
  272.     socket.write("PART " .. currentChannel .. " :Leaving\r\n")
  273.     addMessage("Left channel: " .. currentChannel)
  274.     currentChannel = nil
  275.     displayBuffer()
  276.     return true
  277.   end
  278.   return false
  279. end
  280.  
  281. local function showHelp()
  282.   addMessage("Available commands:")
  283.   addMessage(":helpoc          - Show this help message")
  284.   addMessage("join #channel    - Join an IRC channel")
  285.   addMessage("exit             - Leave the current channel")
  286.   addMessage("disconnect       - Disconnect from the server")
  287.   displayBuffer()
  288. end
  289.  
  290. local function handleUserInput(isCommand)
  291.   local w, h = gpu.getResolution()
  292.   gpu.set(1, h, string.rep(" ", w))
  293.   gpu.set(1, h, isCommand and "Command> " or "Chat> ")
  294.  
  295.   local input = term.read()
  296.   if input then
  297.     input = input:gsub("[\r\n]", "")
  298.     if #input == 0 then displayBuffer() return false end
  299.    
  300.     if isCommand then
  301.       local joinMatch = input:match("^join%s+(#%S+)") or input:match("^connect%s+(#%S+)")
  302.       if joinMatch then
  303.         currentChannel = joinMatch
  304.         socket.write("JOIN " .. currentChannel .. "\r\n")
  305.       elseif input == "disconnect" then
  306.         return disconnectFromServer()
  307.       elseif input == "exit" then
  308.         return exitChannel()
  309.       elseif input == ":helpoc" then
  310.         showHelp()
  311.       else
  312.         socket.write(input .. "\r\n")
  313.       end
  314.     else
  315.       if currentChannel then
  316.         socket.write("PRIVMSG " .. currentChannel .. " :" .. input .. "\r\n")
  317.         addMessage("<" .. username .. "> " .. input)
  318.       else
  319.         addMessage("Not in a channel. Tap Alt and type: join #channel")
  320.       end
  321.     end
  322.     displayBuffer()
  323.   end
  324.   return false
  325. end
  326.  
  327. local function handleServerCommunication()
  328.   addMessage("Connected! Tap L/R Ctrl to type messages, Tap L/R Alt for commands.")
  329.   displayBuffer()
  330.  
  331.   while socket do
  332.     local lines = parseIncomingNetwork()
  333.     if lines then
  334.       for _, line in ipairs(lines) do
  335.         if line:find("^PING") then
  336.           local pingId = line:match("PING :?(.*)")
  337.           socket.write("PONG :" .. pingId .. "\r\n")
  338.         else
  339.           local nick, target, msg = line:match("^:(.-)!.- PRIVMSG%s+(%S+)%s+:(.*)$")
  340.           if nick and msg then
  341.             addMessage("[" .. target .. "] <" .. nick .. "> " .. msg)
  342.           else
  343.             addMessage(line)
  344.           end
  345.         end
  346.       end
  347.       displayBuffer()
  348.     end
  349.  
  350.     local eventName, _, char, code = event.pull(0.05)
  351.     if eventName == "key_down" then
  352.       if code == keyboard.keys.lcontrol or code == keyboard.keys.rcontrol then
  353.         if handleUserInput(false) then return end
  354.       elseif code == keyboard.keys.lmenu or code == keyboard.keys.rmenu then
  355.         if handleUserInput(true) then return end
  356.       elseif code == keyboard.keys.up then
  357.         currentBufferIndex = math.max(1, currentBufferIndex - 1)
  358.         displayBuffer()
  359.       elseif code == keyboard.keys.down then
  360.         currentBufferIndex = math.min(#messageBuffer, currentBufferIndex + 1)
  361.         displayBuffer()
  362.       end
  363.     end
  364.   end
  365. end
  366.  
  367. local function main()
  368.   while true do
  369.     drawServerList()
  370.     local eventName, _, char, code = event.pull("key_down")
  371.  
  372.     if char == 120 or char == 88 then
  373.       term.clear()
  374.       print("Exiting program...")
  375.       break
  376.     elseif code == keyboard.keys.up or code == keyboard.keys.left then
  377.       selectedServerIndex = math.max(1, selectedServerIndex - 1)
  378.     elseif code == keyboard.keys.down or code == keyboard.keys.right then
  379.       selectedServerIndex = math.min(#servers, selectedServerIndex + 1)
  380.     elseif char == 110 or char == 78 then
  381.       username = changeNickname()
  382.     elseif code == keyboard.keys.enter then
  383.       term.clear()
  384.       if connectToServer(servers[selectedServerIndex]) then
  385.         if isColorSupported then
  386.           gpu.setResolution(gpu.maxResolution())
  387.           gpu.setBackground(0x000000)
  388.           gpu.setForeground(0xFFFFFF)
  389.         end
  390.         handleServerCommunication()
  391.       else
  392.         addMessage("Press any key to return to server list.")
  393.         displayBuffer()
  394.         event.pull("key_down")
  395.       end
  396.     end
  397.   end
  398. end
  399.  
  400. main()
Advertisement
Add Comment
Please, Sign In to add comment