Advertisement
Guest User

Untitled

a guest
Jul 20th, 2016
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.48 KB | None | 0 0
  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://irc.freenode.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 = Verti_
  330. local password = nil
  331. local newNick = "Verti__"
  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. if win ~= windows["current"] then
  453. seen[win] = false
  454. end
  455. if legacy then
  456. while #s > w do
  457. table.insert(win,s:sub(1,w))
  458. s = s:sub(w+1)
  459. end
  460. table.insert(win,s)
  461. while #win > BUFFERSIZE do
  462. table.remove(win,1)
  463. end
  464. end
  465. end
  466.  
  467. -- helper
  468. local errormsg = function(msg)
  469. if legacy then
  470. writeToWin(windows["status"],msg)
  471. end
  472. end
  473.  
  474. -- draw the current window to the screen
  475. local drawWin = function(win)
  476. if legacy then
  477. local x,y = term.getCursorPos()
  478. for i = 2,h-2 do
  479. term.setCursorPos(1,i)
  480. term.clearLine()
  481. end
  482. if #win > 0 then
  483. local i = math.max(1,#win-h+4)
  484. iend = #win
  485. local row = 2
  486. while i <= iend do
  487. term.setCursorPos(1,row)
  488. local line = win[i]
  489. --[[
  490. local n = 0
  491. while #line > w do
  492. term.write(line:sub(1,w))
  493. row = row + 1
  494. n = n + 1
  495. term.setCursorPos(1,row)
  496. line = line:sub(w+1)
  497. end
  498. ]]--
  499. term.write(line)
  500. row = row + 1
  501. i = i + 1
  502. end
  503. end
  504. term.setCursorPos(x,y)
  505. end
  506. end
  507.  
  508. -- draw the separator with list of windows and indicators of activity
  509.  
  510. local drawSeparator = function()
  511. if legacy then
  512. local x,y = term.getCursorPos()
  513. local nwin = #winnumbers
  514. --local left = math.floor(0.5*(w-2*nwin+1))
  515. --local right = w-left-2*nwin-1
  516. term.setCursorPos(1,h-1)
  517. term.write(string.rep("-",w))
  518. local newcount = 0
  519. for i = 1,nwin do
  520. if not seen[windows[winnumbers[i]]] then
  521. newcount = newcount + 1
  522. end
  523. end
  524. local start = math.ceil(0.5*(w-(2*nwin+1+newcount)))
  525. term.setCursorPos(start,h-1)
  526. for i=1,nwin do
  527. term.write(" ")
  528. if winnumbers[i] == currentChannel then
  529. term.setBackgroundColor(colors.white)
  530. term.setTextColor(colors.black)
  531. end
  532. term.write(string.format("%d",i))
  533. if not seen[windows[winnumbers[i]]] then
  534. term.write("+")
  535. end
  536. term.setBackgroundColor(colors.black)
  537. term.setTextColor(colors.white)
  538. end
  539. term.write(" ")
  540. term.setCursorPos(x,y)
  541. end
  542. end
  543.  
  544. -- top banner with channel name
  545. local drawBanner = function()
  546. local x,y = term.getCursorPos()
  547. term.setCursorPos(1,1)
  548. term.clearLine()
  549. --local banner = "["..currentChannel.."]"
  550. local banner = currentChannel
  551. local start = math.ceil(0.5*(w-#banner))
  552. term.setCursorPos(start,1)
  553. term.write(banner)
  554. term.setCursorPos(x,y)
  555. end
  556.  
  557. local drawInput = function()
  558. if legacy then
  559. term.setCursorPos(1,h)
  560. term.clearLine()
  561. term.write("> ")
  562. end
  563. end
  564.  
  565. local handleResponse = function(data)
  566. for i = 1, #data do
  567. local id = data[i][2]
  568. if id == "PING" then
  569. pong(data[i][4][1])
  570. elseif id == "PRIVMSG" then
  571. local senderDetails = data[i][3]
  572. local sender = senderDetails:sub(1,senderDetails:find("!")-1)
  573. local channel = data[i][4][1]:lower()
  574. --print(textutils.serialize(data[i]))
  575. --print(channel)
  576. if channel == nick then
  577. channel = sender
  578. if not windows[sender] then
  579. if legacy then
  580. windows[channel] = {}
  581. end
  582. table.insert(winnumbers,channel)
  583. seen[windows[channel]] = false
  584. end
  585. end
  586. local msg = data[i][4][2]
  587. if legacy then
  588. writeToWin(windows[channel],"<"..sender.."> "..msg)
  589. end
  590. --print("<"..sender.."> "..msg)
  591. elseif id == "NICK" then
  592. if newNick ~= "" then
  593. local name = data[i][3]:sub(1,data[i][3]:find("!")-1)
  594. if name == nick then
  595. nick = newNick
  596. newNick = ""
  597. end
  598. end
  599. elseif id == "433" then
  600. writeToWin(windows["status"],"Nickname already in use!")
  601. newNick = ""
  602. elseif codes[id] then
  603. if legacy then
  604. writeToWin(windows["status"],writeLine(data[i][4],""))
  605. --print(data[i][4][2])
  606. end
  607. --print(data[i][4][2])
  608. else
  609. errormsg(writeLine(data[i],""))
  610. end
  611. end
  612. end
  613.  
  614. commands = {}
  615. commands["join"] = function(input)
  616. if not input[2] then
  617. errormsg("No channel specified!")
  618. return true
  619. end
  620. local channel = input[2]:lower()
  621. if channel:sub(1,1) ~= "#" then
  622. errormsg("Invalid channel name!")
  623. return true
  624. end
  625. currentChannel = channel
  626. if legacy then
  627. windows[channel] = {}
  628. end
  629. windows["current"] = windows[currentChannel]
  630. table.insert(winnumbers,channel)
  631. seen[windows[channel]] = true
  632. send("JOIN "..channel)
  633. return true
  634. end
  635.  
  636. commands["who"] = function(input)
  637. local channel = input[2]
  638. send("WHO "..channel)
  639. return true
  640. end
  641.  
  642. commands["whois"] = function(input)
  643. local user = input[2]
  644. send("WHOIS "..user)
  645. return true
  646. end
  647.  
  648. commands["part"] = function(input)
  649. local channel = currentChannel
  650. if channel == "status" then
  651. errormsg("Can't part status window!")
  652. else
  653. local nwin
  654. for i,v in pairs(winnumbers) do
  655. if v == channel then
  656. nwin = i
  657. break
  658. end
  659. end
  660. table.remove(winnumbers,nwin)
  661. seen[windows[channel]] = nil
  662. windows[channel] = nil
  663. currentChannel = winnumbers[#winnumbers]
  664. print(currentChannel)
  665. windows["current"] = windows[currentChannel]
  666. seen[windows["current"]] = true
  667. if channel:sub(1,1) == "#" then
  668. send("PART "..channel)
  669. end
  670. end
  671. return true
  672. end
  673.  
  674. commands["quit"] = function(input)
  675. quit(input[2])
  676. return false
  677. end
  678.  
  679. commands["window"] = function(input)
  680. local nwin = tonumber(input[2])
  681. if not nwin then
  682. errormsg("Invalid window number!")
  683. elseif nwin > #winnumbers or nwin < 1 then
  684. errormsg("Invalid window number!")
  685. else
  686. windows["current"] = windows[winnumbers[nwin]]
  687. seen[windows["current"]] = true
  688. currentChannel = winnumbers[nwin]
  689. end
  690. return true
  691. end
  692.  
  693. commands["nick"] = function(input)
  694. local nickname = input[2]
  695. if nickname then
  696. send("NICK "..nickname)
  697. newNick = nickname
  698. writeToWin(windows["status"],"Changed nick to: "..newNick)
  699. else
  700. errormsg("No nickname given.")
  701. end
  702. return true
  703. end
  704.  
  705. commands["query"] = function(input)
  706. local user = input[2]
  707. if user then
  708. currentChannel = user
  709. if legacy then
  710. windows[user] = {}
  711. end
  712. windows["current"] = windows[currentChannel]
  713. table.insert(winnumbers,user)
  714. seen[windows[user]] = true
  715. --send("JOIN "..channel)
  716. else
  717. errormsg("No username given.")
  718. end
  719. return true
  720. end
  721.  
  722. commands["help"] = function(input)
  723. writeToWin(windows["status"],"Available commands:")
  724. for k,v in pairs(commands) do
  725. writeToWin(windows["status"],"- "..k)
  726. end
  727. return true
  728. end
  729.  
  730. local alias = {}
  731. alias["w"] = "window"
  732. alias["j"] = "join"
  733. alias["p"] = "part"
  734. alias["q"] = "quit"
  735. alias["exit"] = "quit"
  736. alias["e"] = "quit"
  737. alias["n"] = "nick"
  738. alias["qr"] = "query"
  739. alias["h"] = "help"
  740.  
  741.  
  742. local changeWindow = function(nwin)
  743. windows["current"] = windows[winnumbers[nwin]]
  744. seen[windows["current"]] = true
  745. currentChannel = winnumbers[nwin]
  746. return true
  747. end
  748.  
  749. local handeInput = function(input)
  750. if not input then return true end
  751. if input:sub(1,1) == "/" then
  752. input = split(input," ")
  753. local cmd = input[1]:sub(2)
  754. if commands[cmd] then
  755. return commands[cmd](input)
  756. elseif alias[cmd] then
  757. return commands[alias[cmd]](input)
  758. else
  759. errormsg("Invalid command!")
  760. end
  761. elseif currentChannel ~= "status" then
  762. send("PRIVMSG "..currentChannel.." :"..input)
  763. writeToWin(windows["current"],"<"..nick.."> "..input)
  764. else
  765. writeToWin(windows["status"],input)
  766. end
  767. return true
  768. end
  769.  
  770. local keys = {}
  771.  
  772. -- arrow keys cause problems with read() :/
  773.  
  774. --[[
  775. keys[203] = function() -- LEFT
  776. local n = win2num(currentChannel)
  777. if n > 1 then
  778. changeWindow(n-1)
  779. end
  780. return true
  781. end
  782.  
  783. keys[205] = function() -- RIGHT
  784. local n = win2num(currentChannel)
  785. if n < #winnumbers then
  786. changeWindow(n+1)
  787. end
  788. return true
  789. end
  790. ]]--
  791.  
  792. -- TAB to cycle through windows
  793. keys[15] = function()
  794. local n = win2num(currentChannel)
  795. if n < #winnumbers then
  796. changeWindow(n+1)
  797. else
  798. changeWindow(1)
  799. end
  800. return true
  801. end
  802.  
  803. local receive = function()
  804. resp = recv()
  805. while resp do
  806. _data = resp.readAll()
  807. if #_data > 0 then
  808. data = decode(_data)
  809. handleResponse(data)
  810. while lock == "on" do
  811. sleep(0.1)
  812. end
  813. lock = "on"
  814. drawBanner()
  815. drawWin(windows["current"])
  816. drawSeparator()
  817. --drawInput()
  818. lock = "off"
  819. end
  820. resp = recv()
  821. end
  822. quitReason = "Disconnected!"
  823. end
  824.  
  825. local interface = function()
  826. input = read()
  827. while handeInput(input) do
  828. while lock == "on" do
  829. sleep(0.1)
  830. end
  831. lock = "on"
  832. drawBanner()
  833. drawWin(windows["current"])
  834. drawSeparator()
  835. drawInput()
  836. lock = "off"
  837. input = read()
  838. end
  839. quitReason = "Bye!"
  840. end
  841.  
  842. local specialKeys = function()
  843. while true do
  844. local ev, key = os.pullEvent("key")
  845. if keys[key] then
  846. if not keys[key]() then break end
  847. while lock == "on" do
  848. sleep(0.1)
  849. end
  850. lock = "on"
  851. drawBanner()
  852. drawWin(windows["current"])
  853. drawSeparator()
  854. --drawInput()
  855. lock = "off"
  856. end
  857. end
  858. quitReason = "Bye!"
  859. end
  860.  
  861. local args = {...}
  862. if #args > 0 then
  863. if args[1] == "help" then
  864. print("Usage: irc [nick] [password]")
  865. print("Edit the file to change servers.")
  866. return
  867. else
  868. nick = args[1]
  869. password = args[2]
  870. end
  871. end
  872. drawBanner()
  873. drawWin(windows["current"])
  874. drawSeparator()
  875. drawInput()
  876. errormsg("Connecting...")
  877. resp = connect(password)
  878. if not resp then exit("Unable to connect!") return end
  879. _data = resp.readAll()
  880. data = decode(_data)
  881. sessionID = data[2]
  882. errormsg("Got sessionID: "..sessionID)
  883. --sleep(1)
  884. parallel.waitForAny(receive,interface,specialKeys)
  885. exit(quitReason)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement