Advertisement
TheRockettek

Untitled

Apr 15th, 2017
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --[[
  2. IRCCC v 0.9.2 ComputerCraft IRC Client
  3. Matti Vapa, 2014
  4. https://github.com/mattijv/IRCCC/
  5. http://pastebin.com/gaSL8HZC
  6.  
  7. This is a ComputerCraft IRC client, or more specifically a client for the qwebirc backend server.
  8. Unfortunately the HTTP API in ComputerCraft doesn't allow for a true IRC client (even though the
  9. HTTP protocol can in theory be used to communicate with an IRC server[1]). This client provides
  10. a work-around by interfacing with the qwebirc backend. qwebirc is a popular webchat interface and
  11. is relatively straightforward to work with. In addition, several IRC servers already host a webchat
  12. server, so there's no need to set up your own, although that remains a possibility if you can't
  13. find a webchat for your favourite server to connect to.
  14.  
  15. How to use:
  16.  
  17.     * Download with: pastebin get gaSL8HZC irc
  18.     * Check that the baseUrl and dynamicUrl parameters are correct. Examples are provided for espernet
  19.       and quakenet. By default the client connects to espernet.
  20.     * Start the client with:
  21.         irc
  22.  
  23. You can also supply an optional nick argument to set your nickname, or you can change your nick within the client with the standard /nick command.
  24. Currently supported commands are:
  25.  
  26. /window N           - switch to window N
  27. /join CHANNEL       - join CHANNEL
  28. /part               - part the current channel or private chat
  29. /quit               - disconnect and quit
  30. /nick NICK          - change nickname to NICK
  31. /query USER         - start a private chat with user USER
  32. /help               - list available commands
  33.  
  34.  
  35. 1. https://www.youtube.com/watch?v=2ctRfWnisSk#t=343
  36.  
  37. TODO:
  38.         - color support
  39.         - better handling of the IRC protocol
  40.         - use window API where available
  41.  
  42. This program is released under the MIT license.
  43.  
  44. ]]--
  45.  
  46.  
  47. -- look at the source of the qwebirc webchat login page and take the values
  48. -- for baseUrl and dynamicBaseUrl for use here
  49. -- examples for espernet and quakenet
  50.  
  51. local baseUrl = "http://webchat.esper.net/"
  52. local dynamicUrl = ""
  53. --local baseUrl = "http://webchat.quakenet.org/"
  54. --local dynamicUrl = "dynamic/leibniz/"
  55.  
  56.  
  57. --------------------------------------------------------------------------------
  58. --------------------------------------------------------------------------------
  59. -- JSON4Lua: JSON encoding / decoding support for the Lua language.
  60. -- json Module.
  61. -- Author: Craig Mason-Jones
  62. -- Homepage: http://json.luaforge.net/
  63. -- Version: 0.9.40
  64. -- This module is released under the MIT License (MIT).
  65.  
  66. -- edited for brevity
  67.  
  68. local base = _G
  69. local decode_scanArray
  70. local decode_scanComment
  71. local decode_scanConstant
  72. local decode_scanNumber
  73. local decode_scanObject
  74. local decode_scanString
  75. local decode_scanWhitespace
  76. local encodeString
  77. local isArray
  78. local isEncodable
  79.  
  80. local function encode (v)
  81.   -- Handle nil values
  82.   if v==nil then
  83.     return "null"
  84.   end
  85.  
  86.   local vtype = base.type(v)  
  87.  
  88.   -- Handle strings
  89.   if vtype=='string' then    
  90.     return '"' .. encodeString(v) .. '"'            -- Need to handle encoding in string
  91.   end
  92.  
  93.   -- Handle booleans
  94.   if vtype=='number' or vtype=='boolean' then
  95.     return base.tostring(v)
  96.   end
  97.  
  98.   -- Handle tables
  99.   if vtype=='table' then
  100.     local rval = {}
  101.     -- Consider arrays separately
  102.     local bArray, maxCount = isArray(v)
  103.     if bArray then
  104.       for i = 1,maxCount do
  105.         table.insert(rval, encode(v[i]))
  106.       end
  107.     else        -- An object, not an array
  108.       for i,j in base.pairs(v) do
  109.         if isEncodable(i) and isEncodable(j) then
  110.           table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
  111.         end
  112.       end
  113.     end
  114.     if bArray then
  115.       return '[' .. table.concat(rval,',') ..']'
  116.     else
  117.       return '{' .. table.concat(rval,',') .. '}'
  118.     end
  119.   end
  120.  
  121.   -- Handle null values
  122.   if vtype=='function' and v==null then
  123.     return 'null'
  124.   end
  125.  
  126.   base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
  127. end
  128.  
  129. local function decode(s, startPos)
  130.   startPos = startPos and startPos or 1
  131.   startPos = decode_scanWhitespace(s,startPos)
  132.   base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
  133.   local curChar = string.sub(s,startPos,startPos)
  134.   -- Object
  135.   if curChar=='{' then
  136.     return decode_scanObject(s,startPos)
  137.   end
  138.   -- Array
  139.   if curChar=='[' then
  140.     return decode_scanArray(s,startPos)
  141.   end
  142.   -- Number
  143.   if string.find("+-0123456789.e", curChar, 1, true) then
  144.     return decode_scanNumber(s,startPos)
  145.   end
  146.   -- String
  147.   if curChar==[["]] or curChar==[[']] then
  148.    return decode_scanString(s,startPos)
  149.  end
  150.  if string.sub(s,startPos,startPos+1)=='/*' then
  151.    return decode(s, decode_scanComment(s,startPos))
  152.  end
  153.  -- Otherwise, it must be a constant
  154.  return decode_scanConstant(s,startPos)
  155. end
  156.  
  157. local function null()
  158.  return null -- so json.null() will also return null ;-)
  159. end
  160.  
  161.  
  162. function decode_scanArray(s,startPos)
  163.  local array = {}      -- The return value
  164.  local stringLen = string.len(s)
  165.  base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
  166.  startPos = startPos + 1
  167.  -- Infinite loop for array elements
  168.  repeat
  169.    startPos = decode_scanWhitespace(s,startPos)
  170.    base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
  171.    local curChar = string.sub(s,startPos,startPos)
  172.    if (curChar==']') then
  173.      return array, startPos+1
  174.    end
  175.    if (curChar==',') then
  176.      startPos = decode_scanWhitespace(s,startPos+1)
  177.    end
  178.    base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
  179.    object, startPos = decode(s,startPos)
  180.    table.insert(array,object)
  181.  until false
  182. end
  183.  
  184. function decode_scanComment(s, startPos)
  185.  base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
  186.  local endPos = string.find(s,'*/',startPos+2)
  187.  base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
  188.  return endPos+2  
  189. end
  190.  
  191. function decode_scanConstant(s, startPos)
  192.  local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
  193.  local constNames = {"true","false","null"}
  194.  
  195.  for i,k in base.pairs(constNames) do
  196.    --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
  197.    if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
  198.      return consts[k], startPos + string.len(k)
  199.    end
  200.  end
  201.  base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
  202. end
  203.  
  204. function decode_scanNumber(s,startPos)
  205.  local endPos = startPos+1
  206.  local stringLen = string.len(s)
  207.  local acceptableChars = "+-0123456789.e"
  208.  while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
  209.        and endPos<=stringLen
  210.        ) do
  211.    endPos = endPos + 1
  212.  end
  213.  local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
  214.  local stringEval = base.loadstring(stringValue)
  215.  base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
  216.  return stringEval(), endPos
  217. end
  218.  
  219. function decode_scanObject(s,startPos)
  220.  local object = {}
  221.  local stringLen = string.len(s)
  222.  local key, value
  223.  base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
  224.  startPos = startPos + 1
  225.  repeat
  226.    startPos = decode_scanWhitespace(s,startPos)
  227.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
  228.    local curChar = string.sub(s,startPos,startPos)
  229.    if (curChar=='}') then
  230.      return object,startPos+1
  231.    end
  232.    if (curChar==',') then
  233.      startPos = decode_scanWhitespace(s,startPos+1)
  234.    end
  235.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
  236.    -- Scan the key
  237.    key, startPos = decode(s,startPos)
  238.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  239.    startPos = decode_scanWhitespace(s,startPos)
  240.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  241.    base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
  242.    startPos = decode_scanWhitespace(s,startPos+1)
  243.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  244.    value, startPos = decode(s,startPos)
  245.    object[key]=value
  246.  until false   -- infinite loop while key-value pairs are found
  247. end
  248.  
  249. function decode_scanString(s,startPos)
  250.  base.assert(startPos, 'decode_scanString(..) called without start position')
  251.  local startChar = string.sub(s,startPos,startPos)
  252.  base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
  253.   local escaped = false
  254.   local endPos = startPos + 1
  255.   local bEnded = false
  256.   local stringLen = string.len(s)
  257.   repeat
  258.     local curChar = string.sub(s,endPos,endPos)
  259.     -- Character escaping is only used to escape the string delimiters
  260.     if not escaped then
  261.       if curChar==[[\]] then
  262.         escaped = true
  263.       else
  264.         bEnded = curChar==startChar
  265.       end
  266.     else
  267.       -- If we're escaped, we accept the current character come what may
  268.       escaped = false
  269.     end
  270.     endPos = endPos + 1
  271.     base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
  272.   until bEnded
  273.   local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
  274.   local stringEval = base.loadstring(stringValue)
  275.   base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
  276.   return stringEval(), endPos  
  277. end
  278.  
  279. function decode_scanWhitespace(s,startPos)
  280.   local whitespace=" \n\r\t"
  281.   local stringLen = string.len(s)
  282.   while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true)  and startPos <= stringLen) do
  283.     startPos = startPos + 1
  284.   end
  285.   return startPos
  286. end
  287.  
  288. function encodeString(s)
  289.   s = string.gsub(s,'\\','\\\\')
  290.   s = string.gsub(s,'"','\\"')
  291.   s = string.gsub(s,'\n','\\n')
  292.   s = string.gsub(s,'\t','\\t')
  293.   return s
  294. end
  295.  
  296. function isArray(t)
  297.   -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable
  298.   -- (with the possible exception of 'n')
  299.   local maxIndex = 0
  300.   for k,v in base.pairs(t) do
  301.     if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then      -- k,v is an indexed pair
  302.       if (not isEncodable(v)) then return false end     -- All array elements must be encodable
  303.       maxIndex = math.max(maxIndex,k)
  304.     else
  305.       if (k=='n') then
  306.         if v ~= table.getn(t) then return false end  -- False if n does not hold the number of elements
  307.       else -- Else of (k=='n')
  308.         if isEncodable(v) then return false end
  309.       end  -- End of (k~='n')
  310.     end -- End of k,v not an indexed pair
  311.   end  -- End of loop across all pairs
  312.   return true, maxIndex
  313. end
  314.  
  315. function isEncodable(o)
  316.   local t = base.type(o)
  317.   return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)
  318. end
  319.  
  320. --------------------------------------------------------------------------------
  321. --------------------------------------------------------------------------------
  322.  
  323. -- ComputerCraft IRC client code begins here
  324.  
  325. --------------------------------------------------------------------------------
  326. --------------------------------------------------------------------------------
  327.  
  328. local defaultNick = "cc-irc-client"..tostring(math.random(1,1000))
  329. local nick = defaultNick
  330. local password = nil
  331. local newNick = ""
  332.  
  333. local counter = 0 -- used by the qwebirc protocol for something
  334. local sessionID = "" -- as is this, although a bit more self evident
  335.  
  336. local currentChannel = "status"
  337. local quitReason = ""
  338.  
  339. local BUFFERSIZE = 100 -- keep this many lines in each windows history
  340. local offset = 0 -- for scrolling, not implemented
  341.  
  342. local w,h = term.getSize()
  343. local legacy = true --(window == nil) CC v < 1.6 doesn't have the window API which would be useful, but maybe later
  344. local windows = {}
  345. local winnumbers = {}
  346. local seen = {}
  347. if not legacy then
  348.     local origTerm = term.current()
  349.     windows["input"] = window.create(origTerm,1,h-1,w,2)
  350.     windows["status"] = window.create(origTerm,1,1,w,h-2)
  351.     windows["current"] = windows["status"]
  352. else
  353.     windows["status"] = {}
  354.     windows["current"] = windows["status"]
  355. end
  356. table.insert(winnumbers,"status")
  357. seen[windows["status"]] = true
  358. local lock = "off"
  359.  
  360. local win2num = function(win)
  361.     for k,v in pairs(winnumbers) do
  362.         if v == win then return k end
  363.     end
  364.     return nil
  365. end
  366.  
  367. --os.loadAPI("json")
  368.  
  369. local post = function(url,data)
  370.     local cacheAvoidance = "abc"..tostring(math.random(0,10000)) -- not sure if this is needed...
  371.     local resp = http.post(baseUrl..dynamicUrl.."e/"..url,"r="..cacheAvoidance.."&t="..tostring(counter)..data)
  372.     counter = counter + 1
  373.     return resp
  374. end
  375.  
  376. -- better responsiveness with asynchronous methods as we usually let the recv couroutine handle the responses
  377. local request = function(url,data)
  378.     local cacheAvoidance = "abc"..tostring(math.random(0,10000)) -- not sure if this is needed...
  379.     http.request(baseUrl..dynamicUrl.."e/"..url,"r="..cacheAvoidance.."&t="..tostring(counter)..data)
  380.     counter = counter + 1
  381. end
  382.  
  383. -- these special URLs are used by the webchat server for different methods
  384. local send = function(data)
  385.     return request("p","&s="..sessionID.."&c="..textutils.urlEncode(data))
  386. end
  387.  
  388. local recv = function()
  389.     return post("s","&s="..sessionID)
  390. end
  391.  
  392. local connect = function(password)
  393.   if password ~= nil then
  394.     return post("n","&nick="..nick.."&password="..password)
  395.   else
  396.     return post("n","&nick="..nick)
  397.   end
  398. end
  399.  
  400. -- some helper functions
  401. local pong = function(data)
  402.     return send("PONG :"..data)
  403. end
  404.  
  405. local quit = function(_reason)
  406.     local reason = _reason or ""
  407.     return send("QUIT :"..reason)
  408. end
  409.  
  410. -- lua default string methods suck :/
  411. local split = function(str,sep)
  412.         local sep, fields = sep or ":", {}
  413.         local pattern = string.format("([^%s]+)", sep)
  414.         str:gsub(pattern, function(c) fields[#fields+1] = c end)
  415.         return fields
  416. end
  417.  
  418. local exit = function(reason)
  419.     local r = reason or ""
  420.     term.clear()
  421.     term.setCursorPos(1,1)
  422.     print(r)
  423. end
  424.  
  425. -- for debug
  426. local writeLine
  427. writeLine = function(data,line)
  428.     if not (type(data) == "table") then
  429.         return tostring(data)
  430.     end
  431.     for i=1,#data do
  432.         if type(data[i]) == "table" then
  433.             line = writeLine(data[i],line)
  434.         elseif data[i] ~= nick then
  435.             line = line..data[i].." "
  436.         end
  437.     end
  438.     return line
  439. end
  440.  
  441. -- some IRC protocol codes we handle "properly"
  442. local codes = {}
  443. codes["371"] = "RPL_INFO"
  444. codes["374"] = "RPL_ENDINFO"
  445. codes["375"] = "RPL_MOTDSTART"
  446. codes["372"] = "RPL_MOTD"
  447. codes["376"] = "RPL_ENDOFMOTD"
  448. codes["352"] = "RPL_WHOREPLY"
  449.  
  450. -- add lines to window
  451. local writeToWin = function(win, s)
  452.     print(s)
  453. end
  454.  
  455. -- helper
  456. local errormsg = function(msg)
  457.     if legacy then
  458.         writeToWin(windows["status"],msg)
  459.     end
  460. end
  461.  
  462. -- draw the current window to the screen
  463. local drawWin = function(win)
  464.     if legacy then
  465.         local x,y = term.getCursorPos()
  466.         for i = 2,h-2 do
  467.             term.setCursorPos(1,i)
  468.             term.clearLine()
  469.         end
  470.         if #win > 0 then
  471.             local i = math.max(1,#win-h+4)
  472.             iend = #win
  473.             local row = 2
  474.             while i <= iend do
  475.                 term.setCursorPos(1,row)
  476.                 local line = win[i]
  477.                 --[[
  478.                 local n = 0
  479.                 while #line > w do
  480.                     term.write(line:sub(1,w))
  481.                     row = row + 1
  482.                     n = n + 1
  483.                     term.setCursorPos(1,row)
  484.                     line = line:sub(w+1)
  485.                 end
  486.                 ]]--
  487.                 term.write(line)
  488.                 row = row + 1
  489.                 i = i + 1
  490.             end
  491.         end
  492.         term.setCursorPos(x,y)
  493.     end
  494. end
  495.  
  496. -- draw the separator with list of windows and indicators of activity
  497.  
  498. local drawSeparator = function()
  499.     if legacy then
  500.         local x,y = term.getCursorPos()
  501.         local nwin = #winnumbers
  502.         --local left = math.floor(0.5*(w-2*nwin+1))
  503.         --local right = w-left-2*nwin-1
  504.         term.setCursorPos(1,h-1)
  505.         term.write(string.rep("-",w))
  506.         local newcount = 0
  507.         for i = 1,nwin do
  508.             if not seen[windows[winnumbers[i]]] then
  509.                 newcount = newcount + 1
  510.             end
  511.         end
  512.         local start = math.ceil(0.5*(w-(2*nwin+1+newcount)))
  513.         term.setCursorPos(start,h-1)
  514.         for i=1,nwin do
  515.             term.write(" ")
  516.             if winnumbers[i] == currentChannel then
  517.                 term.setBackgroundColor(colors.white)
  518.                 term.setTextColor(colors.black)
  519.             end
  520.             term.write(string.format("%d",i))
  521.             if not seen[windows[winnumbers[i]]] then
  522.                 term.write("+")
  523.             end
  524.             term.setBackgroundColor(colors.black)
  525.             term.setTextColor(colors.white)
  526.         end
  527.         term.write(" ")
  528.         term.setCursorPos(x,y)
  529.     end
  530. end
  531.  
  532. -- top banner with channel name
  533. local drawBanner = function()
  534.     local x,y = term.getCursorPos()
  535.     term.setCursorPos(1,1)
  536.     term.clearLine()
  537.     --local banner = "["..currentChannel.."]"
  538.     local banner = currentChannel
  539.     local start = math.ceil(0.5*(w-#banner))
  540.     term.setCursorPos(start,1)
  541.     term.write(banner)
  542.     term.setCursorPos(x,y)
  543. end
  544.  
  545. local drawInput = function()
  546.     if legacy then
  547.         term.setCursorPos(1,h)
  548.         term.clearLine()
  549.         term.write("> ")
  550.     end
  551. end
  552.  
  553. local handleResponse = function(data)
  554.     for i = 1, #data do
  555.         local id = data[i][2]
  556.         if id == "PING" then
  557.             pong(data[i][4][1])
  558.         elseif id == "PRIVMSG" then
  559.             local senderDetails = data[i][3]
  560.             local sender = senderDetails:sub(1,senderDetails:find("!")-1)
  561.             local channel = data[i][4][1]:lower()
  562.       --print(textutils.serialize(data[i]))
  563.       --print(channel)
  564.             if channel == nick then
  565.                 channel = sender
  566.                 if not windows[sender] then
  567.                     if legacy then
  568.                         windows[channel] = {}
  569.                     end
  570.                     table.insert(winnumbers,channel)
  571.                     seen[windows[channel]] = false
  572.                 end
  573.             end
  574.             local msg = data[i][4][2]
  575.             if legacy then
  576.                 writeToWin(windows[channel],"<"..sender.."> "..msg)
  577.             end
  578.             --print("<"..sender.."> "..msg)
  579.         elseif id == "NICK" then
  580.             if newNick ~= "" then
  581.                 local name = data[i][3]:sub(1,data[i][3]:find("!")-1)
  582.                 if name == nick then
  583.                     nick = newNick
  584.                     newNick = ""
  585.                 end
  586.             end
  587.         elseif id == "433" then
  588.             writeToWin(windows["status"],"Nickname already in use!")
  589.             newNick = ""
  590.         elseif codes[id] then
  591.             if legacy then
  592.                 writeToWin(windows["status"],writeLine(data[i][4],""))
  593.                 --print(data[i][4][2])
  594.             end
  595.             --print(data[i][4][2])
  596.         else
  597.             errormsg(writeLine(data[i],""))
  598.         end
  599.     end
  600. end
  601.  
  602. commands = {}
  603. commands["join"] = function(input)
  604.   if not input[2] then
  605.     errormsg("No channel specified!")
  606.     return true
  607.   end
  608.     local channel = input[2]:lower()
  609.     if channel:sub(1,1) ~= "#" then
  610.         errormsg("Invalid channel name!")
  611.         return true
  612.     end
  613.     currentChannel = channel
  614.     if legacy then
  615.         windows[channel] = {}
  616.     end
  617.     windows["current"] = windows[currentChannel]
  618.     table.insert(winnumbers,channel)
  619.     seen[windows[channel]] = true
  620.     send("JOIN "..channel)
  621.     return true
  622. end
  623.  
  624. commands["who"] = function(input)
  625.     local channel = input[2]
  626.     send("WHO "..channel)
  627.     return true
  628. end
  629.  
  630. commands["whois"] = function(input)
  631.     local user = input[2]
  632.     send("WHOIS "..user)
  633.     return true
  634. end
  635.  
  636. commands["part"] = function(input)
  637.     local channel = currentChannel
  638.     if channel == "status" then
  639.         errormsg("Can't part status window!")
  640.     else
  641.         local nwin
  642.         for i,v in pairs(winnumbers) do
  643.             if v == channel then
  644.                 nwin = i
  645.                 break
  646.             end
  647.         end
  648.         table.remove(winnumbers,nwin)
  649.         seen[windows[channel]] = nil
  650.         windows[channel] = nil
  651.         currentChannel = winnumbers[#winnumbers]
  652.         print(currentChannel)
  653.         windows["current"] = windows[currentChannel]
  654.         seen[windows["current"]] = true
  655.         if channel:sub(1,1) == "#" then
  656.             send("PART "..channel)
  657.         end
  658.     end
  659.     return true
  660. end
  661.  
  662. commands["quit"] = function(input)
  663.     quit(input[2])
  664.     return false
  665. end
  666.  
  667. commands["window"] = function(input)
  668.     local nwin = tonumber(input[2])
  669.     if not nwin then
  670.         errormsg("Invalid window number!")
  671.     elseif nwin > #winnumbers or nwin < 1 then
  672.         errormsg("Invalid window number!")
  673.     else
  674.         windows["current"] = windows[winnumbers[nwin]]
  675.         seen[windows["current"]] = true
  676.         currentChannel = winnumbers[nwin]
  677.     end
  678.     return true
  679. end
  680.  
  681. commands["nick"] = function(input)
  682.     local nickname = input[2]
  683.     if nickname then
  684.         send("NICK "..nickname)
  685.         newNick = nickname
  686.         writeToWin(windows["status"],"Changed nick to: "..newNick)
  687.     else
  688.         errormsg("No nickname given.")
  689.     end
  690.     return true
  691. end
  692.  
  693. commands["query"] = function(input)
  694.     local user = input[2]
  695.     if user then
  696.         currentChannel = user
  697.         if legacy then
  698.             windows[user] = {}
  699.         end
  700.         windows["current"] = windows[currentChannel]
  701.         table.insert(winnumbers,user)
  702.         seen[windows[user]] = true
  703.         --send("JOIN "..channel)
  704.     else
  705.         errormsg("No username given.")
  706.     end
  707.     return true
  708. end
  709.  
  710. commands["help"] = function(input)
  711.     writeToWin(windows["status"],"Available commands:")
  712.     for k,v in pairs(commands) do
  713.         writeToWin(windows["status"],"- "..k)
  714.     end
  715.     return true
  716. end
  717.  
  718. local alias = {}
  719. alias["w"] = "window"
  720. alias["j"] = "join"
  721. alias["p"] = "part"
  722. alias["q"] = "quit"
  723. alias["exit"] = "quit"
  724. alias["e"] = "quit"
  725. alias["n"] = "nick"
  726. alias["qr"] = "query"
  727. alias["h"] = "help"
  728.  
  729.  
  730. local changeWindow = function(nwin)
  731.     windows["current"] = windows[winnumbers[nwin]]
  732.     seen[windows["current"]] = true
  733.     currentChannel = winnumbers[nwin]
  734.     return true
  735. end
  736.  
  737. local handeInput = function(input)
  738.     if not input then return true end
  739.     if input:sub(1,1) == "/" then
  740.         input = split(input," ")
  741.         local cmd = input[1]:sub(2)
  742.         if commands[cmd] then
  743.             return commands[cmd](input)
  744.         elseif alias[cmd] then
  745.             return commands[alias[cmd]](input)
  746.         else
  747.             errormsg("Invalid command!")
  748.         end
  749.     elseif currentChannel ~= "status" then
  750.             send("PRIVMSG "..currentChannel.." :"..input)
  751.             writeToWin(windows["current"],"<"..nick.."> "..input)
  752.     else
  753.         writeToWin(windows["status"],input)
  754.     end
  755.     return true
  756. end
  757.  
  758. local keys = {}
  759.  
  760. -- arrow keys cause problems with read() :/
  761.  
  762. --[[
  763. keys[203] = function() -- LEFT
  764.     local n = win2num(currentChannel)
  765.     if n > 1 then
  766.         changeWindow(n-1)
  767.     end
  768.     return true
  769. end
  770.  
  771. keys[205] = function() -- RIGHT
  772.     local n = win2num(currentChannel)
  773.     if n < #winnumbers then
  774.         changeWindow(n+1)
  775.     end
  776.     return true
  777. end
  778. ]]--
  779.  
  780. -- TAB to cycle through windows
  781. keys[15] = function()
  782.     local n = win2num(currentChannel)
  783.     if n < #winnumbers then
  784.         changeWindow(n+1)
  785.     else
  786.         changeWindow(1)
  787.     end
  788.     return true
  789. end
  790.  
  791. local receive = function()
  792.     resp = recv()
  793.     while resp do
  794.         _data = resp.readAll()
  795.         if #_data > 0 then
  796.             data = decode(_data)
  797.             handleResponse(data)
  798.             while lock == "on" do
  799.                 sleep(0.1)
  800.             end
  801.             lock = "on"
  802.             drawBanner()
  803.             drawWin(windows["current"])
  804.             drawSeparator()
  805.             --drawInput()
  806.             lock = "off"
  807.         end
  808.         resp = recv()
  809.     end
  810.     quitReason = "Disconnected!"
  811. end
  812.  
  813. local interface = function()
  814.     input = read()
  815.     while handeInput(input) do
  816.         while lock == "on" do
  817.             sleep(0.1)
  818.         end
  819.         lock = "on"
  820.         drawBanner()
  821.         drawWin(windows["current"])
  822.         drawSeparator()
  823.         drawInput()
  824.         lock = "off"
  825.         input = read()
  826.     end
  827.     quitReason = "Bye!"
  828. end
  829.  
  830. local specialKeys = function()
  831.     while true do
  832.         local ev, key = os.pullEvent("key")
  833.         if keys[key] then
  834.             if not keys[key]() then break end
  835.             while lock == "on" do
  836.                 sleep(0.1)
  837.             end
  838.             lock = "on"
  839.             drawBanner()
  840.             drawWin(windows["current"])
  841.             drawSeparator()
  842.             --drawInput()
  843.             lock = "off"
  844.         end
  845.     end
  846.     quitReason = "Bye!"
  847. end
  848.  
  849. local args = {...}
  850. if #args > 0 then
  851.     if args[1] == "help" then
  852.         print("Usage: irc [nick] [password]")
  853.         print("Edit the file to change servers.")
  854.         return
  855.     else
  856.         nick = args[1]
  857.     password = args[2]
  858.     end
  859. end
  860. drawBanner()
  861. drawWin(windows["current"])
  862. drawSeparator()
  863. drawInput()
  864. errormsg("Connecting...")
  865. resp = connect(password)
  866. if not resp then exit("Unable to connect!") return end
  867. _data = resp.readAll()
  868. data = decode(_data)
  869. sessionID = data[2]
  870. errormsg("Got sessionID: "..sessionID)
  871. --sleep(1)
  872. parallel.waitForAny(receive,interface,specialKeys)
  873. exit(quitReason)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement