Advertisement
SirBaconBitz

Test

Jul 22nd, 2015
268
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.21 KB | None | 0 0
  1. local component = require("component")
  2. local computer = require("computer")
  3. local event = require("event")
  4. local internet = require("internet")
  5. local shell = require("shell")
  6. local term = require("term")
  7. local text = require("text")
  8.  
  9. local nick = "SirBaconBot"
  10. local host = "irc.esper.net:6667"
  11.  
  12. if not component.isAvailable("internet") then
  13.   io.stderr:write("Put in an internet card you dumbfuck")
  14.   return
  15. end
  16.  
  17. -- try to connect to server.
  18. local sock, reason = internet.open(host)
  19. if not sock then
  20.   io.stderr:write(reason .. "\n")
  21.   return
  22. end
  23.  
  24. -- custom print that uses all except the last line for printing.
  25. local function print(message, overwrite)
  26.   local w, h = component.gpu.getResolution()
  27.   local line
  28.   repeat
  29.     line, message = text.wrap(text.trim(message), w, w)
  30.     if not overwrite then
  31.       component.gpu.copy(1, 1, w, h - 1, 0, -1)
  32.     end
  33.     overwrite = false
  34.     component.gpu.fill(1, h - 1, w, 1, " ")
  35.     component.gpu.set(1, h - 1, line)
  36.   until not message or message == ""
  37. end
  38.  
  39. -- utility method for reply tracking tables.
  40. function autocreate(table, key)
  41.   table[key] = {}
  42.   return table[key]
  43. end
  44.  
  45. -- extract nickname from identity.
  46. local function name(identity)
  47.   return identity and identity:match("^[^!]+") or identity or "Anonymous"
  48. end
  49.  
  50. -- user defined callback for messages (via `lua function(msg) ... end`)
  51. local callback = nil
  52.  
  53. -- list of whois info per user (used to accumulate whois replies).
  54. local whois = setmetatable({}, {__index=autocreate})
  55.  
  56. -- list of users per channel (used to accumulate names replies).
  57. local names = setmetatable({}, {__index=autocreate})
  58.  
  59. -- timer used to drive socket reading.
  60. local timer
  61.  
  62. -- ignored commands, reserved according to RFC.
  63. -- http://tools.ietf.org/html/rfc2812#section-5.3
  64. local ignore = {
  65.   [213]=true, [214]=true, [215]=true, [216]=true, [217]=true,
  66.   [218]=true, [231]=true, [232]=true, [233]=true, [240]=true,
  67.   [241]=true, [244]=true, [244]=true, [246]=true, [247]=true,
  68.   [250]=true, [300]=true, [316]=true, [361]=true, [362]=true,
  69.   [363]=true, [373]=true, [384]=true, [492]=true,
  70.   -- custom ignored responses.
  71.   [265]=true, [266]=true, [330]=true
  72. }
  73.  
  74. -- command numbers to names.
  75. local commands = {
  76. --Replys
  77.   RPL_WELCOME = "001",
  78.   RPL_YOURHOST = "002",
  79.   RPL_CREATED = "003",
  80.   RPL_MYINFO = "004",
  81.   RPL_BOUNCE = "005",
  82.   RPL_LUSERCLIENT = "251",
  83.   RPL_LUSEROP = "252",
  84.   RPL_LUSERUNKNOWN = "253",
  85.   RPL_LUSERCHANNELS = "254",
  86.   RPL_LUSERME = "255",
  87.   RPL_AWAY = "301",
  88.   RPL_UNAWAY = "305",
  89.   RPL_NOWAWAY = "306",
  90.   RPL_WHOISUSER = "311",
  91.   RPL_WHOISSERVER = "312",
  92.   RPL_WHOISOPERATOR = "313",
  93.   RPL_WHOISIDLE = "317",
  94.   RPL_ENDOFWHOIS = "318",
  95.   RPL_WHOISCHANNELS = "319",
  96.   RPL_CHANNELMODEIS = "324",
  97.   RPL_NOTOPIC = "331",
  98.   RPL_TOPIC = "332",
  99.   RPL_NAMREPLY = "353",
  100.   RPL_ENDOFNAMES = "366",
  101.   RPL_MOTDSTART = "375",
  102.   RPL_MOTD = "372",
  103.   RPL_ENDOFMOTD = "376",
  104.   RPL_WHOISSECURE = "671",
  105.   RPL_HELPSTART = "704",
  106.   RPL_HELPTXT = "705",
  107.   RPL_ENDOFHELP = "706",
  108.   RPL_UMODEGMSG = "718",
  109.  
  110. --Errors
  111.   ERR_BANLISTFULL = "478",
  112.   ERR_CHANNELISFULL = "471",
  113.   ERR_UNKNOWNMODE = "472",
  114.   ERR_INVITEONLYCHAN = "473",
  115.   ERR_BANNEDFROMCHAN = "474",
  116.   ERR_CHANOPRIVSNEEDED = "482",
  117.   ERR_UNIQOPRIVSNEEDED = "485",
  118.   ERR_USERNOTINCHANNEL = "441",
  119.   ERR_NOTONCHANNEL = "442",
  120.   ERR_NICKCOLLISION = "436",
  121.   ERR_NICKNAMEINUSE = "433",
  122.   ERR_ERRONEUSNICKNAME = "432",
  123.   ERR_WASNOSUCHNICK = "406",
  124.   ERR_TOOMANYCHANNELS = "405",
  125.   ERR_CANNOTSENDTOCHAN = "404",
  126.   ERR_NOSUCHCHANNEL = "403",
  127.   ERR_NOSUCHNICK = "401",
  128.   ERR_MODELOCK = "742"
  129. }
  130.  
  131. -- main command handling callback.
  132. local function handleCommand(prefix, command, args, message)
  133.   ---------------------------------------------------
  134.   -- Keepalive
  135.  
  136.   if command == "PING" then
  137.     sock:write(string.format("PONG :%s\r\n", message))
  138.     sock:flush()
  139.  
  140.   ---------------------------------------------------
  141.   -- General commands
  142.   elseif command == "NICK" then
  143.     local oldNick, newNick = name(prefix), tostring(args[1] or message)
  144.     if oldNick == nick then
  145.       nick = newNick
  146.     end
  147.     print(oldNick .. " is now known as " .. newNick .. ".")
  148.   elseif command == "MODE" then
  149.     if #args == 2 then
  150.       print("[" .. args[1] .. "] " .. name(prefix) .. " set mode".. ( #args[2] > 2 and "s" or "" ) .. " " .. tostring(args[2] or message) .. ".")
  151.     else
  152.       local setmode = {}
  153.       local cumode = "+"
  154.       args[2]:gsub(".", function(char)
  155.         if char == "-" or char == "+" then
  156.           cumode = char
  157.         else
  158.           table.insert(setmode, {cumode, char})
  159.         end
  160.       end)
  161.       local d = {}
  162.       local users = {}
  163.       for i = 3, #args do
  164.         users[i-2] = args[i]
  165.       end
  166.       users[#users+1] = message
  167.       local last
  168.       local ctxt = ""
  169.       for c = 1, #users do
  170.         if not setmode[c] then
  171.           break
  172.         end
  173.         local mode = setmode[c][2]
  174.         local pfx = setmode[c][1]=="+"
  175.         local key = mode == "o" and (pfx and "opped" or "deoped") or
  176.           mode == "v" and (pfx and "voiced" or "devoiced") or
  177.           mode == "q" and (pfx and "quieted" or "unquieted") or
  178.           mode == "b" and (pfx and "banned" or "unbanned") or
  179.           "set " .. setmode[c][1] .. mode .. " on"
  180.         if last ~= key then
  181.           if last then
  182.             print(ctxt)
  183.           end
  184.           ctxt = "[" .. args[1] .. "] " .. name(prefix) .. " " .. key
  185.           last = key
  186.         end
  187.         ctxt = ctxt .. " " .. users[c]
  188.       end
  189.       if #ctxt > 0 then
  190.         print(ctxt)
  191.       end
  192.     end
  193.   elseif command == "QUIT" then
  194.     print(name(prefix) .. " quit (" .. (message or "Quit") .. ").")
  195.   elseif command == "JOIN" then
  196.     print("[" .. args[1] .. "] " .. name(prefix) .. " entered the room.")
  197.   elseif command == "PART" then
  198.     print("[" .. args[1] .. "] " .. name(prefix) .. " has left the room (quit: " .. (message or "Quit") .. ").")
  199.   elseif command == "TOPIC" then
  200.     print("[" .. args[1] .. "] " .. name(prefix) .. " has changed the topic to: " .. message)
  201.   elseif command == "KICK" then
  202.     print("[" .. args[1] .. "] " .. name(prefix) .. " kicked " .. args[2])
  203.   elseif command == "PRIVMSG" then
  204.     local ctcp = message:match("^\1(.-)\1$")
  205.     if ctcp then
  206.       print("[" .. name(prefix) .. "] CTCP " .. ctcp)
  207.       local ctcp, param = ctcp:match("^(%S+) ?(.-)$")
  208.       ctcp = ctcp:upper()
  209.       if ctcp == "TIME" then
  210.         sock:write("NOTICE " .. name(prefix) .. " :\001TIME " .. os.date() .. "\001\r\n")
  211.         sock:flush()
  212.       elseif ctcp == "VERSION" then
  213.         sock:write("NOTICE " .. name(prefix) .. " :\001VERSION Minecraft/OpenComputers Lua 5.2\001\r\n")
  214.         sock:flush()
  215.       elseif ctcp == "PING" then
  216.         sock:write("NOTICE " .. name(prefix) .. " :\001PING " .. param .. "\001\r\n")
  217.         sock:flush()
  218.       end
  219.     else
  220.       if string.find(message, nick) then
  221.         computer.beep()
  222.       end
  223.       if string.find(message, "\001ACTION") then
  224.         print("[" .. args[1] .. "] " .. name(prefix) .. string.gsub(string.gsub(message, "\001ACTION", ""), "\001", ""))
  225.       else
  226.         print("[" .. args[1] .. "] " .. name(prefix) .. ": " .. message)
  227.       end
  228.     end
  229.   elseif command == "NOTICE" then
  230.     print("[NOTICE] " .. message)
  231.   elseif command == "ERROR" then
  232.     print("[ERROR] " .. message)
  233.  
  234.   ---------------------------------------------------
  235.   -- Ignored reserved numbers
  236.   -- -- http://tools.ietf.org/html/rfc2812#section-5.3
  237.  
  238.   elseif tonumber(command) and ignore[tonumber(command)] then
  239.     -- ignore
  240.  
  241.   ---------------------------------------------------
  242.   -- Command replies
  243.   -- http://tools.ietf.org/html/rfc2812#section-5.1
  244.  
  245.   elseif command == commands.RPL_WELCOME then
  246.     print(message)
  247.   elseif command == commands.RPL_YOURHOST then -- ignore
  248.   elseif command == commands.RPL_CREATED then -- ignore
  249.   elseif command == commands.RPL_MYINFO then -- ignore
  250.   elseif command == commands.RPL_BOUNCE then -- ignore
  251.   elseif command == commands.RPL_LUSERCLIENT then
  252.     print(message)
  253.   elseif command == commands.RPL_LUSEROP then -- ignore
  254.   elseif command == commands.RPL_LUSERUNKNOWN then -- ignore
  255.   elseif command == commands.RPL_LUSERCHANNELS then -- ignore
  256.   elseif command == commands.RPL_LUSERME then
  257.     print(message)
  258.   elseif command == commands.RPL_AWAY then
  259.     print(string.format("%s is away: %s", name(args[1]), message))
  260.   elseif command == commands.RPL_UNAWAY or command == commands.RPL_NOWAWAY then
  261.     print(message)
  262.   elseif command == commands.RPL_WHOISUSER then
  263.     local nick = args[2]:lower()
  264.     whois[nick].nick = args[2]
  265.     whois[nick].user = args[3]
  266.     whois[nick].host = args[4]
  267.     whois[nick].realName = message
  268.   elseif command == commands.RPL_WHOISSERVER then
  269.     local nick = args[2]:lower()
  270.     whois[nick].server = args[3]
  271.     whois[nick].serverInfo = message
  272.   elseif command == commands.RPL_WHOISOPERATOR then
  273.     local nick = args[2]:lower()
  274.     whois[nick].isOperator = true
  275.   elseif command == commands.RPL_WHOISIDLE then
  276.     local nick = args[2]:lower()
  277.     whois[nick].idle = tonumber(args[3])
  278.   elseif command == commands.RPL_WHOISSECURE then
  279.     local nick = args[2]:lower()
  280.     whois[nick].secureconn = "Is using a secure connection"
  281.   elseif command == commands.RPL_ENDOFWHOIS then
  282.     local nick = args[2]:lower()
  283.     local info = whois[nick]
  284.     if info.nick then print("Nick: " .. info.nick) end
  285.     if info.user then print("User name: " .. info.user) end
  286.     if info.realName then print("Real name: " .. info.realName) end
  287.     if info.host then print("Host: " .. info.host) end
  288.     if info.server then print("Server: " .. info.server .. (info.serverInfo and (" (" .. info.serverInfo .. ")") or "")) end
  289.     if info.secureconn then print(info.secureconn) end
  290.     if info.channels then print("Channels: " .. info.channels) end
  291.     if info.idle then print("Idle for: " .. info.idle) end
  292.     whois[nick] = nil
  293.   elseif command == commands.RPL_WHOISCHANNELS then
  294.     local nick = args[2]:lower()
  295.     whois[nick].channels = message
  296.   elseif command == commands.RPL_CHANNELMODEIS then
  297.     print("Channel mode for " .. args[1] .. ": " .. args[2] .. " (" .. args[3] .. ")")
  298.   elseif command == commands.RPL_NOTOPIC then
  299.     print("No topic is set for " .. args[1] .. ".")
  300.   elseif command == commands.RPL_TOPIC then
  301.     print("Topic for " .. args[1] .. ": " .. message)
  302.   elseif command == commands.RPL_NAMREPLY then
  303.     local channel = args[3]
  304.     table.insert(names[channel], message)
  305.   elseif command == commands.RPL_ENDOFNAMES then
  306.     local channel = args[2]
  307.     print("Users on " .. channel .. ": " .. (#names[channel] > 0 and table.concat(names[channel], " ") or "none"))
  308.     names[channel] = nil
  309.   elseif command == commands.RPL_MOTDSTART then
  310.     if options.motd then
  311.       print(message .. args[1])
  312.     end
  313.   elseif command == commands.RPL_MOTD then
  314.     if options.motd then
  315.       print(message)
  316.     end
  317.   elseif command == commands.RPL_ENDOFMOTD then -- ignore
  318.   elseif command == commands.RPL_HELPSTART or
  319.   command == commands.RPL_HELPTXT or
  320.   command == commands.RPL_ENDOFHELP then
  321.     print(message)
  322.   elseif command == commands.ERR_BANLISTFULL or
  323.   command == commands.ERR_BANNEDFROMCHAN or
  324.   command == commands.ERR_CANNOTSENDTOCHAN or
  325.   command == commands.ERR_CHANNELISFULL or
  326.   command == commands.ERR_CHANOPRIVSNEEDED or
  327.   command == commands.ERR_ERRONEUSNICKNAME or
  328.   command == commands.ERR_INVITEONLYCHAN or
  329.   command == commands.ERR_NICKCOLLISION or
  330.   command == commands.ERR_NOSUCHNICK or
  331.   command == commands.ERR_NOTONCHANNEL or
  332.   command == commands.ERR_UNIQOPRIVSNEEDED or
  333.   command == commands.ERR_UNKNOWNMODE or
  334.   command == commands.ERR_USERNOTINCHANNEL or
  335.   command == commands.ERR_WASNOSUCHNICK or
  336.   command == commands.ERR_MODELOCK then
  337.     print("[ERROR]: " .. message)
  338.   elseif tonumber(command) and (tonumber(command) >= 200 and tonumber(command) < 400) then
  339.     print("[Response " .. command .. "] " .. table.concat(args, ", ") .. ": " .. message)
  340.  
  341.   ---------------------------------------------------
  342.   -- Error messages. No real point in handling those manually.
  343.   -- http://tools.ietf.org/html/rfc2812#section-5.2
  344.  
  345.   elseif tonumber(command) and (tonumber(command) >= 400 and tonumber(command) < 600) then
  346.     print("[Error] " .. table.concat(args, ", ") .. ": " .. message)
  347.  
  348.   ---------------------------------------------------
  349.   -- Unhandled message.
  350.  
  351.   else
  352.     print("Unhandled command: " .. command .. ": " .. message)
  353.   end
  354. end
  355.  
  356. -- catch errors to allow manual closing of socket and removal of timer.
  357. local result, reason = pcall(function()
  358.   -- say hello.
  359.   term.clear()
  360.   print("Welcome to OpenIRC!")
  361.  
  362.   -- avoid sock:read locking up the computer.
  363.   sock:setTimeout(0.05)
  364.  
  365.   -- http://tools.ietf.org/html/rfc2812#section-3.1
  366.   sock:write(string.format("NICK %s\r\n", nick))
  367.   sock:write(string.format("USER %s 0 * :%s [OpenComputers]\r\n", nick:lower(), nick))
  368.   sock:flush()
  369.  
  370.   -- socket reading logic (receive messages) driven by a timer.
  371.   timer = event.timer(0.5, function()
  372.     if not sock then
  373.       return false
  374.     end
  375.     repeat
  376.       local ok, line = pcall(sock.read, sock)
  377.       if ok then
  378.         if not line then
  379.           print("Connection lost.")
  380.           sock:close()
  381.           sock = nil
  382.           return false
  383.         end
  384.         line = text.trim(line) -- get rid of trailing \r
  385.         local match, prefix = line:match("^(:(%S+) )")
  386.         if match then line = line:sub(#match + 1) end
  387.         local match, command = line:match("^(([^:]%S*))")
  388.         if match then line = line:sub(#match + 1) end
  389.         local args = {}
  390.         repeat
  391.           local match, arg = line:match("^( ([^:]%S*))")
  392.           if match then
  393.             line = line:sub(#match + 1)
  394.             table.insert(args, arg)
  395.           end
  396.         until not match
  397.         local message = line:match("^ :(.*)$")
  398.  
  399.         if callback then
  400.           local result, reason = pcall(callback, prefix, command, args, message)
  401.           if not result then
  402.             print("Error in callback: " .. tostring(reason))
  403.           end
  404.         end
  405.         handleCommand(prefix, command, args, message)
  406.       end
  407.     until not ok
  408.   end, math.huge)
  409.  
  410.   -- default target for messages, so we don't have to type /msg all the time.
  411.   local target = nil
  412.  
  413.   -- command history.
  414.   local history = {}
  415.  
  416.   repeat
  417.     local w, h = component.gpu.getResolution()
  418.     term.setCursor(1, h)
  419.     term.write((target or "?") .. "> ")
  420.     local line = term.read(history)
  421.     if sock and line and line ~= "" then
  422.       line = text.trim(line)
  423.       if line:lower():sub(1,4) == "/me " then
  424.         print("[" .. (target or "?") .. "] " .. nick .. " " .. line:sub(5), true)
  425.       elseif line~="" then
  426.         print("[" .. (target or "?") .. "] " .. nick .. ": " .. line, true)
  427.       end
  428.       if line:lower():sub(1, 5) == "/msg " then
  429.         local user, message = line:sub(6):match("^(%S+) (.+)$")
  430.         if message then
  431.           message = text.trim(message)
  432.         end
  433.         if not user or not message or message == "" then
  434.           print("Invalid use of /msg. Usage: /msg nick|channel message.")
  435.           line = ""
  436.         else
  437.           target = user
  438.           line = "PRIVMSG " .. target .. " :" .. message
  439.         end
  440.       elseif line:lower():sub(1, 6) == "/join " then
  441.         local channel = text.trim(line:sub(7))
  442.         if not channel or channel == "" then
  443.           print("Invalid use of /join. Usage: /join channel.")
  444.           line = ""
  445.         else
  446.           target = channel
  447.           line = "JOIN " .. channel
  448.         end
  449.       elseif line:lower():sub(1, 5) == "/lua " then
  450.         local script = text.trim(line:sub(6))
  451.         local result, reason = load(script, "=stdin", nil, setmetatable({print=print, socket=sock, nick=nick}, {__index=_G}))
  452.         if not result then
  453.           result, reason = load("return " .. script, "=stdin", nil, setmetatable({print=print, socket=sock, nick=nick}, {__index=_G}))
  454.         end
  455.         line = ""
  456.         if not result then
  457.           print("Error: " .. tostring(reason))
  458.         else
  459.           result, reason = pcall(result)
  460.           if not result then
  461.             print("Error: " .. tostring(reason))
  462.           elseif type(reason) == "function" then
  463.             callback = reason
  464.           elseif reason then
  465.             line = tostring(reason)
  466.           end
  467.         end
  468.       elseif line:lower():sub(1,4) == "/me " then
  469.         if not target then
  470.           print("No default target set. Use /msg or /join to set one.")
  471.           line = ""
  472.         else
  473.           line = "PRIVMSG " .. target .. " :\001ACTION " .. line:sub(5) .. "\001"
  474.         end
  475.       elseif line:sub(1, 1) == "/" then
  476.         line = line:sub(2)
  477.       elseif line ~= "" then
  478.         if not target then
  479.           print("No default target set. Use /msg or /join to set one.")
  480.           line = ""
  481.         else
  482.           line = "PRIVMSG " .. target .. " :" .. line
  483.         end
  484.       end
  485.       if line and line ~= "" then
  486.         sock:write(line .. "\r\n")
  487.         sock:flush()
  488.       end
  489.     end
  490.   until not sock or not line
  491. end)
  492.  
  493. if sock then
  494.   sock:write("QUIT\r\n")
  495.   sock:close()
  496. end
  497. if timer then
  498.   event.cancel(timer)
  499. end
  500.  
  501. if not result then
  502.   error(reason, 0)
  503. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement