Advertisement
Guest User

IRC_ROBOT

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