Advertisement
nonogamer9

DEV

Sep 20th, 2024 (edited)
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.19 KB | None | 0 0
  1. local component = require("component")
  2. local event = require("event")
  3. local term = require("term")
  4. local unicode = require("unicode")
  5. local serialization = require("serialization")
  6. local filesystem = require("filesystem")
  7. local keyboard = require("keyboard")
  8. local gpu = component.gpu
  9. local computer = require("computer")
  10.  
  11. local internet = component.internet
  12. if not internet then error("No internet card found. Please install an Internet Card.") end
  13.  
  14. local CONFIG_FILE = "/home/irc_config.cfg"
  15.  
  16. local default_config = {
  17. server = "irc.libera.chat",
  18. port = 6667,
  19. nickname = "OCBot",
  20. username = "OCUser",
  21. realname = "OpenComputers IRC User",
  22. channel = "#opencomputers"
  23. }
  24.  
  25. local function loadConfig()
  26. if filesystem.exists(CONFIG_FILE) then
  27. local file = io.open(CONFIG_FILE, "r")
  28. if file then
  29. local content = file:read("*all")
  30. file:close()
  31. return serialization.unserialize(content) or default_config
  32. end
  33. end
  34. return default_config
  35. end
  36.  
  37. local function saveConfig(config)
  38. local file = io.open(CONFIG_FILE, "w")
  39. if file then
  40. file:write(serialization.serialize(config))
  41. file:close()
  42. print("Configuration saved successfully.")
  43. else
  44. print("Error: Unable to save configuration.")
  45. end
  46. end
  47.  
  48. local function configMenu(config)
  49. while true do
  50. term.clear()
  51. print("IRC Client Configuration")
  52. print("------------------------")
  53. print("1. Server: " .. config.server)
  54. print("2. Port: " .. config.port)
  55. print("3. Nickname: " .. config.nickname)
  56. print("4. Username: " .. config.username)
  57. print("5. Real Name: " .. config.realname)
  58. print("6. Channel: " .. config.channel)
  59. print("7. Save and Exit")
  60. print("8. Exit without saving")
  61.  
  62. io.write("Enter your choice (1-8): ")
  63. local choice = io.read()
  64.  
  65. if choice == "1" then
  66. io.write("Enter new server: ")
  67. config.server = io.read()
  68. elseif choice == "2" then
  69. io.write("Enter new port: ")
  70. config.port = tonumber(io.read()) or config.port
  71. elseif choice == "3" then
  72. io.write("Enter new nickname: ")
  73. config.nickname = io.read()
  74. elseif choice == "4" then
  75. io.write("Enter new username: ")
  76. config.username = io.read()
  77. elseif choice == "5" then
  78. io.write("Enter new real name: ")
  79. config.realname = io.read()
  80. elseif choice == "6" then
  81. io.write("Enter new channel: ")
  82. config.channel = io.read()
  83. elseif choice == "7" then
  84. saveConfig(config)
  85. break
  86. elseif choice == "8" then
  87. break
  88. end
  89. end
  90. end
  91.  
  92. -- UI Helper Functions
  93. local function drawBox(x, y, width, height, color)
  94. local oldColor = gpu.getBackground()
  95. gpu.setBackground(color)
  96. gpu.fill(x, y, width, height, " ")
  97. gpu.setBackground(oldColor)
  98. end
  99.  
  100. local function drawText(x, y, text, fgColor, bgColor)
  101. local oldFg, oldBg = gpu.getForeground(), gpu.getBackground()
  102. gpu.setForeground(fgColor)
  103. gpu.setBackground(bgColor)
  104. gpu.set(x, y, text)
  105. gpu.setForeground(oldFg)
  106. gpu.setBackground(oldBg)
  107. end
  108.  
  109. -- UI Components
  110. local function createChatArea(x, y, width, height)
  111. local messages = {}
  112. local scrollPosition = 0
  113.  
  114. local function addMessage(msg)
  115. table.insert(messages, msg)
  116. if #messages > height then
  117. table.remove(messages, 1)
  118. end
  119. scrollPosition = math.max(0, #messages - height)
  120. end
  121.  
  122. local function draw()
  123. drawBox(x, y, width, height, 0x000000)
  124. for i = 1, height do
  125. local msg = messages[i + scrollPosition]
  126. if msg then
  127. drawText(x, y + i - 1, unicode.sub(msg, 1, width), 0xFFFFFF, 0x000000)
  128. end
  129. end
  130. end
  131.  
  132. return {
  133. addMessage = addMessage,
  134. draw = draw
  135. }
  136. end
  137.  
  138. local function createUserList(x, y, width, height)
  139. local users = {}
  140.  
  141. local function updateUsers(newUsers)
  142. users = {}
  143. for user in newUsers do
  144. table.insert(users, user)
  145. end
  146. end
  147.  
  148. local function draw()
  149. drawBox(x, y, width, height, 0x0000FF)
  150. drawText(x, y, "Users", 0xFFFFFF, 0x0000FF)
  151. for i, user in ipairs(users) do
  152. if i > height - 1 then break end
  153. drawText(x, y + i, user, 0xFFFFFF, 0x0000FF)
  154. end
  155. end
  156.  
  157. return {
  158. updateUsers = updateUsers,
  159. draw = draw
  160. }
  161. end
  162.  
  163. local function createInputArea(x, y, width)
  164. local input = ""
  165. local cursorPos = 0
  166.  
  167. local function handleKey(char, code)
  168. if char > 0 then
  169. input = unicode.sub(input, 1, cursorPos) .. unicode.char(char) .. unicode.sub(input, cursorPos + 1)
  170. cursorPos = cursorPos + 1
  171. elseif code == keyboard.keys.back and cursorPos > 0 then
  172. input = unicode.sub(input, 1, cursorPos - 1) .. unicode.sub(input, cursorPos + 1)
  173. cursorPos = cursorPos - 1
  174. elseif code == keyboard.keys.left and cursorPos > 0 then
  175. cursorPos = cursorPos - 1
  176. elseif code == keyboard.keys.right and cursorPos < unicode.len(input) then
  177. cursorPos = cursorPos + 1
  178. end
  179. end
  180.  
  181. local function draw()
  182. drawBox(x, y, width, 1, 0x000000)
  183. drawText(x, y, unicode.sub(input, 1, width), 0xFFFFFF, 0x000000)
  184. gpu.set(x + cursorPos, y, "_")
  185. end
  186.  
  187. local function getText()
  188. local text = input
  189. input = ""
  190. cursorPos = 0
  191. return text
  192. end
  193.  
  194. return {
  195. handleKey = handleKey,
  196. draw = draw,
  197. getText = getText
  198. }
  199. end
  200.  
  201. local function runClient(config)
  202. local width, height = gpu.getResolution()
  203. local chatArea = createChatArea(1, 1, width - 20, height - 2)
  204. local userList = createUserList(width - 19, 1, 20, height - 2)
  205. local inputArea = createInputArea(1, height, width)
  206.  
  207. local socket = internet.connect(config.server, config.port)
  208. if not socket then
  209. error("Failed to connect to IRC server")
  210. end
  211.  
  212. local function send(msg)
  213. socket.write(msg .. "\r\n")
  214. chatArea.addMessage("Sent: " .. msg)
  215. end
  216.  
  217. local registered = false
  218. local nick = config.nickname
  219. local username = config.username
  220. local realname = config.realname
  221. local nickAttempts = 0
  222. local connectionAttempts = 0
  223. local MAX_CONNECTION_ATTEMPTS = 3
  224.  
  225. local function register()
  226. send("USER " .. username .. " 0 * :" .. realname)
  227. send("NICK " .. nick)
  228. end
  229.  
  230. local function handleNickError()
  231. nickAttempts = nickAttempts + 1
  232. nick = config.nickname .. nickAttempts
  233. chatArea.addMessage("Nickname in use or connection incomplete, trying " .. nick)
  234. send("NICK " .. nick)
  235. end
  236.  
  237. local function connect()
  238. while connectionAttempts < MAX_CONNECTION_ATTEMPTS do
  239. connectionAttempts = connectionAttempts + 1
  240. chatArea.addMessage("Connection attempt " .. connectionAttempts)
  241.  
  242. register()
  243.  
  244. -- Wait for registration confirmation or error
  245. local timeout = computer.uptime() + 10 -- 10 second timeout
  246. while computer.uptime() < timeout do
  247. local data = socket.read()
  248. if data then
  249. for line in data:gmatch("[^\r\n]+") do
  250. chatArea.addMessage("Received: " .. line)
  251. if line:match("^:.*001") then
  252. registered = true
  253. return true
  254. elseif line:match("451.*finish connecting") then
  255. handleNickError()
  256. os.sleep(2) -- Add a small delay before retrying
  257. break
  258. end
  259. end
  260. end
  261. os.sleep(0.1)
  262. end
  263.  
  264. if registered then
  265. return true
  266. end
  267.  
  268. chatArea.addMessage("Connection attempt failed, retrying...")
  269. os.sleep(5) -- Wait 5 seconds before next attempt
  270. end
  271. return false
  272. end
  273.  
  274. if not connect() then
  275. chatArea.addMessage("Failed to connect after " .. MAX_CONNECTION_ATTEMPTS .. " attempts")
  276. return
  277. end
  278.  
  279. local function handleServerMessage(prefix, command, params)
  280. if not params then
  281. chatArea.addMessage("Warning: Received message with nil params")
  282. return
  283. end
  284.  
  285. if command == "001" then
  286. registered = true
  287. chatArea.addMessage("Successfully registered as " .. nick)
  288. send("JOIN " .. config.channel)
  289. elseif command == "433" then
  290. if not registered then
  291. handleNickError()
  292. end
  293. elseif command == "353" then
  294. if params[#params] then
  295. local users = params[#params]:gmatch("%S+")
  296. userList.updateUsers(users)
  297. else
  298. chatArea.addMessage("Warning: Invalid user list received")
  299. end
  300. elseif command == "PRIVMSG" then
  301. if #params >= 2 then
  302. local target, message = params[1], params[2]
  303. local sender = prefix:match("^([^!]+)")
  304. if target == config.channel then
  305. chatArea.addMessage("<" .. sender .. "> " .. message)
  306. else
  307. chatArea.addMessage("*" .. sender .. "* " .. message)
  308. end
  309. else
  310. chatArea.addMessage("Warning: Invalid PRIVMSG format")
  311. end
  312. elseif command == "JOIN" then
  313. local joiner = prefix:match("^([^!]+)")
  314. chatArea.addMessage("* " .. joiner .. " has joined " .. (params[1] or "unknown channel"))
  315. elseif command == "PART" then
  316. local leaver = prefix:match("^([^!]+)")
  317. chatArea.addMessage("* " .. leaver .. " has left " .. (params[1] or "unknown channel"))
  318. elseif command == "QUIT" then
  319. local quitter = prefix:match("^([^!]+)")
  320. chatArea.addMessage("* " .. quitter .. " has quit")
  321. end
  322. end
  323.  
  324. local running = true
  325. local lastDrawTime = 0
  326.  
  327. local function processIncomingMessages()
  328. local data = socket.read()
  329. if data then
  330. for line in data:gmatch("[^\r\n]+") do
  331. chatArea.addMessage("Received: " .. line)
  332.  
  333. if line:match("^PING :(.+)") then
  334. local server = line:match("^PING :(.+)")
  335. send("PONG :" .. server)
  336. else
  337. local prefix, command, params = line:match("^(:(%S+) )?(%S+)(.*)")
  338. if prefix then prefix = prefix:sub(2) end
  339. params = params and params:gsub("^%s*", "") or ""
  340. local paramTable = {}
  341. for param in params:gmatch("([^%s:]+)") do
  342. table.insert(paramTable, param)
  343. end
  344. if params:match(" :") then
  345. table.insert(paramTable, params:match(" :(.*)$"))
  346. end
  347. handleServerMessage(prefix, command, paramTable)
  348. end
  349. end
  350. end
  351. end
  352.  
  353. while running do
  354. local currentTime = computer.uptime()
  355. if currentTime - lastDrawTime >= 0.1 then -- Redraw every 0.1 seconds
  356. chatArea.draw()
  357. userList.draw()
  358. inputArea.draw()
  359. lastDrawTime = currentTime
  360. end
  361.  
  362. local eventType, _, char, code = event.pull(0.05)
  363. if eventType == "key_down" then
  364. if char == 13 then -- Enter key
  365. local message = inputArea.getText()
  366. if message ~= "" then
  367. send("PRIVMSG " .. config.channel .. " :" .. message)
  368. chatArea.addMessage("<" .. nick .. "> " .. message)
  369. end
  370. else
  371. inputArea.handleKey(char, code)
  372. end
  373. elseif eventType == "interrupted" then
  374. running = false
  375. end
  376.  
  377. processIncomingMessages()
  378. end
  379.  
  380. socket.close()
  381. gpu.setBackground(0x000000)
  382. gpu.setForeground(0xFFFFFF)
  383. term.clear()
  384. print("Disconnected from IRC server.")
  385. end
  386.  
  387. local logo = [[
  388. U ___ u ____ ____ ____
  389. \/"_ \/U /"___| ___ U | _"\ u U /"___|
  390. | | | |\| | u U u |_"_| \| |_) |/ \| | u
  391. .-,_| |_| | | |/__ /___\ | | | _ < | |/__
  392. \_)-\___/ \____|__"__|U/| |\u |_| \_\ \____|
  393. \\ _// \\ .-,_|___|_,-.// \\_ _// \\
  394. (__) (__)(__) \_)-' '-(_/(__) (__)(__)(__)
  395. ]]
  396.  
  397. local function main()
  398. term.clear()
  399. print(logo)
  400.  
  401. local config = loadConfig()
  402.  
  403. while true do
  404. print("\nIRC Client Menu")
  405. print("1. Start IRC Client")
  406. print("2. Edit Configuration")
  407. print("3. Exit")
  408.  
  409. io.write("Enter your choice (1-3): ")
  410. local choice = io.read()
  411.  
  412. if choice == "1" then
  413. print("Connecting to " .. config.server .. "...")
  414. runClient(config)
  415. elseif choice == "2" then
  416. configMenu(config)
  417. elseif choice == "3" then
  418. break
  419. end
  420. end
  421.  
  422. print("Goodbye!")
  423. end
  424.  
  425. local status, err = pcall(main)
  426. if not status then
  427. print("An error occurred:")
  428. print(err)
  429. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement