Advertisement
BruceWplays

Chat noti

Oct 12th, 2022 (edited)
443
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.33 KB | None | 0 0
  1. local tArgs = { ... }
  2.  
  3. local function printUsage()
  4.     print("Usages:")
  5.     print("chat host <hostname>")
  6.     print("chat join <hostname> <nickname>")
  7. end
  8.  
  9. os.loadAPI("AndysPrograms/api/noti/noti")
  10.  
  11. local sOpenedModem = nil
  12. local function openModem()
  13.     for _, sModem in ipairs(peripheral.getNames()) do
  14.         if peripheral.getType(sModem) == "modem" then
  15.             if not rednet.isOpen(sModem) then
  16.                 rednet.open(sModem)
  17.                 sOpenedModem = sModem
  18.             end
  19.             return true
  20.         end
  21.     end
  22.     print("No modems found.")
  23.     return false
  24. end
  25.  
  26. local function closeModem()
  27.     if sOpenedModem ~= nil then
  28.         rednet.close(sOpenedModem)
  29.         sOpenedModem = nil
  30.     end
  31. end
  32.  
  33. -- Colours
  34. local highlightColour, textColour
  35. if term.isColour() then
  36.     textColour = colours.white
  37.     highlightColour = colours.yellow
  38. else
  39.     textColour = colours.white
  40.     highlightColour = colours.white
  41. end
  42.  
  43. local sCommand = tArgs[1]
  44. if sCommand == "host" then
  45.     -- "chat host"
  46.     -- Get hostname
  47.     local sHostname = tArgs[2]
  48.     if sHostname == nil then
  49.         printUsage()
  50.         return
  51.     end
  52.  
  53.     -- Host server
  54.     if not openModem() then
  55.         return
  56.     end
  57.     rednet.host("chat", sHostname)
  58.     print("0 users connected.")
  59.  
  60.     local tUsers = {}
  61.     local nUsers = 0
  62.     local function send(sText, nUserID)
  63.         if nUserID then
  64.             local tUser = tUsers[nUserID]
  65.             if tUser then
  66.                 rednet.send(tUser.nID, {
  67.                     sType = "text",
  68.                     nUserID = nUserID,
  69.                     sText = sText,
  70.                 }, "chat")
  71.             end
  72.         else
  73.             for nUserID, tUser in pairs(tUsers) do
  74.                 rednet.send(tUser.nID, {
  75.                     sType = "text",
  76.                     nUserID = nUserID,
  77.                     sText = sText,
  78.                 }, "chat")
  79.             end
  80.         end
  81.     end
  82.  
  83.     -- Setup ping pong
  84.     local tPingPongTimer = {}
  85.     local function ping(nUserID)
  86.         local tUser = tUsers[nUserID]
  87.         rednet.send(tUser.nID, {
  88.             sType = "ping to client",
  89.             nUserID = nUserID,
  90.         }, "chat")
  91.  
  92.         local timer = os.startTimer(15)
  93.         tUser.bPingPonged = false
  94.         tPingPongTimer[timer] = nUserID
  95.     end
  96.  
  97.     local function printUsers()
  98.         local _, y = term.getCursorPos()
  99.         term.setCursorPos(1, y - 1)
  100.         term.clearLine()
  101.         if nUsers == 1 then
  102.             print(nUsers .. " user connected.")
  103.         else
  104.             print(nUsers .. " users connected.")
  105.         end
  106.     end
  107.  
  108.     -- Handle messages
  109.     local ok, error = pcall(parallel.waitForAny,
  110.         function()
  111.             while true do
  112.                 local _, timer = os.pullEvent("timer")
  113.                 local nUserID = tPingPongTimer[timer]
  114.                 if nUserID and tUsers[nUserID] then
  115.                     local tUser = tUsers[nUserID]
  116.                     if tUser then
  117.                         if not tUser.bPingPonged then
  118.                             send("* " .. tUser.sUsername .. " has timed out")
  119.                             tUsers[nUserID] = nil
  120.                             nUsers = nUsers - 1
  121.                             printUsers()
  122.                         else
  123.                             ping(nUserID)
  124.                         end
  125.                     end
  126.                 end
  127.             end
  128.         end,
  129.         function()
  130.             while true do
  131.                 local tCommands
  132.                 tCommands = {
  133.                     ["me"] = function(tUser, sContent)
  134.                         if #sContent > 0 then
  135.                             send("* " .. tUser.sUsername .. " " .. sContent)
  136.                         else
  137.                             send("* Usage: /me [words]", tUser.nUserID)
  138.                         end
  139.                     end,
  140.                     ["nick"] = function(tUser, sContent)
  141.                         if #sContent > 0 then
  142.                             local sOldName = tUser.sUsername
  143.                             tUser.sUsername = sContent
  144.                             send("* " .. sOldName .. " is now known as " .. tUser.sUsername)
  145.                         else
  146.                             send("* Usage: /nick [nickname]", tUser.nUserID)
  147.                         end
  148.                     end,
  149.                     ["users"] = function(tUser, sContent)
  150.                         send("* Connected Users:", tUser.nUserID)
  151.                         local sUsers = "*"
  152.                         for _, tUser in pairs(tUsers) do
  153.                             sUsers = sUsers .. " " .. tUser.sUsername
  154.                         end
  155.                         send(sUsers, tUser.nUserID)
  156.                     end,
  157.                     ["help"] = function(tUser, sContent)
  158.                         send("* Available commands:", tUser.nUserID)
  159.                         local sCommands = "*"
  160.                         for sCommand in pairs(tCommands) do
  161.                             sCommands = sCommands .. " /" .. sCommand
  162.                         end
  163.                         send(sCommands .. " /logout", tUser.nUserID)
  164.                     end,
  165.                 }
  166.  
  167.                 local nSenderID, tMessage = rednet.receive("chat")
  168.                 if type(tMessage) == "table" then
  169.                     if tMessage.sType == "login" then
  170.                         -- Login from new client
  171.                         local nUserID = tMessage.nUserID
  172.                         local sUsername = tMessage.sUsername
  173.                         if nUserID and sUsername then
  174.                             tUsers[nUserID] = {
  175.                                 nID = nSenderID,
  176.                                 nUserID = nUserID,
  177.                                 sUsername = sUsername,
  178.                             }
  179.                             nUsers = nUsers + 1
  180.                             printUsers()
  181.                             send("* " .. sUsername .. " has joined the chat")
  182.                             ping(nUserID)
  183.                         end
  184.  
  185.                     else
  186.                         -- Something else from existing client
  187.                         local nUserID = tMessage.nUserID
  188.                         local tUser = tUsers[nUserID]
  189.                         if tUser and tUser.nID == nSenderID then
  190.                             if tMessage.sType == "logout" then
  191.                                 send("* " .. tUser.sUsername .. " has left the chat")
  192.                                 tUsers[nUserID] = nil
  193.                                 nUsers = nUsers - 1
  194.                                 printUsers()
  195.  
  196.                             elseif tMessage.sType == "chat" then
  197.                                 local sMessage = tMessage.sText
  198.                                 if sMessage then
  199.                                     local sCommand = string.match(sMessage, "^/([a-z]+)")
  200.                                     if sCommand then
  201.                                         local fnCommand = tCommands[sCommand]
  202.                                         if fnCommand then
  203.                                             local sContent = string.sub(sMessage, #sCommand + 3)
  204.                                             fnCommand(tUser, sContent)
  205.                                         else
  206.                                             send("* Unrecognised command: /" .. sCommand, tUser.nUserID)
  207.                                         end
  208.                                     else
  209.                                         send("<" .. tUser.sUsername .. "> " .. tMessage.sText)
  210.                                     end
  211.                                 end
  212.  
  213.                             elseif tMessage.sType == "ping to server" then
  214.                                 rednet.send(tUser.nID, {
  215.                                     sType = "pong to client",
  216.                                     nUserID = nUserID,
  217.                                 }, "chat")
  218.  
  219.                             elseif tMessage.sType == "pong to server" then
  220.                                 tUser.bPingPonged = true
  221.  
  222.                             end
  223.                         end
  224.                     end
  225.                  end
  226.             end
  227.         end
  228.    )
  229.     if not ok then
  230.         printError(error)
  231.     end
  232.  
  233.     -- Unhost server
  234.     for nUserID, tUser in pairs(tUsers) do
  235.         rednet.send(tUser.nID, {
  236.             sType = "kick",
  237.             nUserID = nUserID,
  238.         }, "chat")
  239.     end
  240.     rednet.unhost("chat")
  241.     closeModem()
  242.  
  243. elseif sCommand == "join" then
  244.     -- "chat join"
  245.     -- Get hostname and username
  246.     local sHostname = tArgs[2]
  247.     local sUsername = tArgs[3]
  248.     if sHostname == nil or sUsername == nil then
  249.         printUsage()
  250.         return
  251.     end
  252.  
  253.     -- Connect
  254.     if not openModem() then
  255.         return
  256.     end
  257.     write("Looking up " .. sHostname .. "... ")
  258.     local nHostID = rednet.lookup("chat", sHostname)
  259.     if nHostID == nil then
  260.         print("Failed.")
  261.         return
  262.     else
  263.         print("Success.")
  264.     end
  265.  
  266.     -- Login
  267.     local nUserID = math.random(1, 2147483647)
  268.     rednet.send(nHostID, {
  269.         sType = "login",
  270.         nUserID = nUserID,
  271.         sUsername = sUsername,
  272.     }, "chat")
  273.  
  274.     -- Setup ping pong
  275.     local bPingPonged = true
  276.     local pingPongTimer = os.startTimer(0)
  277.  
  278.     local function ping()
  279.         rednet.send(nHostID, {
  280.             sType = "ping to server",
  281.             nUserID = nUserID,
  282.         }, "chat")
  283.         bPingPonged = false
  284.         pingPongTimer = os.startTimer(30)
  285.     end
  286.  
  287.     -- Handle messages
  288.     local w, h = term.getSize()
  289.     local parentTerm = term.current()
  290.     local titleWindow = window.create(parentTerm, 1, 1, w, 1, true)
  291.     local historyWindow = window.create(parentTerm, 1, 2, w, h - 2, true)
  292.     local promptWindow = window.create(parentTerm, 1, h, w, 1, true)
  293.     historyWindow.setCursorPos(1, h - 2)
  294.  
  295.     term.clear()
  296.     term.setTextColour(textColour)
  297.     term.redirect(promptWindow)
  298.     promptWindow.restoreCursor()
  299.  
  300.     local function drawTitle()
  301.         local w = titleWindow.getSize()
  302.         local sTitle = sUsername .. " on " .. sHostname
  303.         titleWindow.setTextColour(highlightColour)
  304.         titleWindow.setCursorPos(math.floor(w / 2 - #sTitle / 2), 1)
  305.         titleWindow.clearLine()
  306.         titleWindow.write(sTitle)
  307.         promptWindow.restoreCursor()
  308.     end
  309.  
  310.     local function printMessage(sMessage)
  311.         term.redirect(historyWindow)
  312.         print()
  313.         if string.match(sMessage, "^%*") then
  314.             -- Information
  315.             term.setTextColour(highlightColour)
  316.             write(sMessage)
  317.             term.setTextColour(textColour)
  318.         else
  319.             -- Chat
  320.             local sUsernameBit = string.match(sMessage, "^<[^>]*>")
  321.             if sUsernameBit then
  322.                 term.setTextColour(highlightColour)
  323.                 write(sUsernameBit)
  324.                 term.setTextColour(textColour)
  325.                 write(string.sub(sMessage, #sUsernameBit + 1))
  326.             else
  327.                 write(sMessage)
  328.             end
  329.         end
  330.         term.redirect(promptWindow)
  331.         promptWindow.restoreCursor()
  332.     end
  333.  
  334.     drawTitle()
  335.  
  336. function printnoti(sText2)
  337.     printMessage(sText2)
  338.     noti.playnoti()
  339. end
  340.  
  341.     local ok, error = pcall(parallel.waitForAny,
  342.         function()
  343.             while true do
  344.                 local sEvent, timer = os.pullEvent()
  345.                 if sEvent == "timer" then
  346.                     if timer == pingPongTimer then
  347.                         if not bPingPonged then
  348.                             printMessage("Server timeout.")
  349.                             return
  350.                         else
  351.                             ping()
  352.                         end
  353.                     end
  354.  
  355.                 elseif sEvent == "term_resize" then
  356.                     local w, h = parentTerm.getSize()
  357.                     titleWindow.reposition(1, 1, w, 1)
  358.                     historyWindow.reposition(1, 2, w, h - 2)
  359.                     promptWindow.reposition(1, h, w, 1)
  360.  
  361.                 end
  362.             end
  363.         end,
  364.         function()
  365.             while true do
  366.                 local nSenderID, tMessage = rednet.receive("chat")
  367.                 if nSenderID == nHostID and type(tMessage) == "table" and tMessage.nUserID == nUserID then
  368.                     if tMessage.sType == "text" then
  369.                         local sText = tMessage.sText
  370.                         if sText then
  371.                             printnoti(sText)
  372.                         end
  373.  
  374.                     elseif tMessage.sType == "ping to client" then
  375.                         rednet.send(nSenderID, {
  376.                             sType = "pong to server",
  377.                             nUserID = nUserID,
  378.                         }, "chat")
  379.  
  380.                     elseif tMessage.sType == "pong to client" then
  381.                         bPingPonged = true
  382.  
  383.                     elseif tMessage.sType == "kick" then
  384.                         return
  385.  
  386.                     end
  387.                 end
  388.             end
  389.         end,
  390.         function()
  391.             local tSendHistory = {}
  392.             while true do
  393.                 promptWindow.setCursorPos(1, 1)
  394.                 promptWindow.clearLine()
  395.                 promptWindow.setTextColor(highlightColour)
  396.                 promptWindow.write(": ")
  397.                 promptWindow.setTextColor(textColour)
  398.  
  399.                 local sChat = read(nil, tSendHistory)
  400.                 if string.match(sChat, "^/logout") then
  401.                     break
  402.                 else
  403.                     rednet.send(nHostID, {
  404.                         sType = "chat",
  405.                         nUserID = nUserID,
  406.                         sText = sChat,
  407.                     }, "chat")
  408.                     table.insert(tSendHistory, sChat)
  409.                 end
  410.             end
  411.         end
  412.     )
  413.  
  414.     -- Close the windows
  415.     term.redirect(parentTerm)
  416.  
  417.     -- Print error notice
  418.     local _, h = term.getSize()
  419.     term.setCursorPos(1, h)
  420.     term.clearLine()
  421.     term.setCursorBlink(false)
  422.     if not ok then
  423.         printError(error)
  424.     end
  425.  
  426.     -- Logout
  427.     rednet.send(nHostID, {
  428.         sType = "logout",
  429.         nUserID = nUserID,
  430.     }, "chat")
  431.     closeModem()
  432.  
  433.     -- Print disconnection notice
  434.     print("Disconnected.")
  435.  
  436. else
  437.     -- "chat somethingelse"
  438.     printUsage()
  439.  
  440. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement