Advertisement
Guest User

Untitled

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