PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/init.lua

http://github.com/JakobOvrum/LuaIRC
Lua | 233 lines | 185 code | 48 blank | 0 comment | 24 complexity | bad7f6e435fe020479ef7be639a435ca MD5 | raw file
  1. local socket = require "socket"
  2. local error = error
  3. local setmetatable = setmetatable
  4. local rawget = rawget
  5. local unpack = unpack
  6. local pairs = pairs
  7. local assert = assert
  8. local require = require
  9. local tonumber = tonumber
  10. local type = type
  11. local pcall = pcall
  12. module "irc"
  13. local meta = {}
  14. meta.__index = meta
  15. _META = meta
  16. require "irc.util"
  17. require "irc.asyncoperations"
  18. require "irc.handlers"
  19. local meta_preconnect = {}
  20. function meta_preconnect.__index(o, k)
  21. local v = rawget(meta_preconnect, k)
  22. if not v and meta[k] then
  23. error(("field '%s' is not accessible before connecting"):format(k), 2)
  24. end
  25. return v
  26. end
  27. function new(data)
  28. local o = {
  29. nick = assert(data.nick, "Field 'nick' is required");
  30. username = data.username or "lua";
  31. realname = data.realname or "Lua owns";
  32. nickGenerator = data.nickGenerator or defaultNickGenerator;
  33. hooks = {};
  34. track_users = true;
  35. }
  36. assert(checkNick(o.nick), "Erroneous nickname passed to irc.new")
  37. return setmetatable(o, meta_preconnect)
  38. end
  39. function meta:hook(name, id, f)
  40. f = f or id
  41. self.hooks[name] = self.hooks[name] or {}
  42. self.hooks[name][id] = f
  43. return id or f
  44. end
  45. meta_preconnect.hook = meta.hook
  46. function meta:unhook(name, id)
  47. local hooks = self.hooks[name]
  48. assert(hooks, "no hooks exist for this event")
  49. assert(hooks[id], "hook ID not found")
  50. hooks[id] = nil
  51. end
  52. meta_preconnect.unhook = meta.unhook
  53. function meta:invoke(name, ...)
  54. local hooks = self.hooks[name]
  55. if hooks then
  56. for id,f in pairs(hooks) do
  57. if f(...) then
  58. return true
  59. end
  60. end
  61. end
  62. end
  63. function meta_preconnect:connect(_host, _port)
  64. local host, port, password, secure, timeout
  65. if type(_host) == "table" then
  66. host = _host.host
  67. port = _host.port
  68. timeout = _host.timeout
  69. password = _host.password
  70. secure = _host.secure
  71. else
  72. host = _host
  73. port = _port
  74. end
  75. host = host or error("host name required to connect", 2)
  76. port = port or 6667
  77. local s = socket.tcp()
  78. s:settimeout(timeout or 30)
  79. assert(s:connect(host, port))
  80. if secure then
  81. local work, ssl = pcall(require, "ssl")
  82. if not work then
  83. error("LuaSec required for secure connections", 2)
  84. end
  85. local params
  86. if type(secure) == "table" then
  87. params = secure
  88. else
  89. params = {mode = "client", protocol = "tlsv1"}
  90. end
  91. s = ssl.wrap(s, params)
  92. success, errmsg = s:dohandshake()
  93. if not success then
  94. error(("could not make secure connection: %s"):format(errmsg), 2)
  95. end
  96. end
  97. self.socket = s
  98. setmetatable(self, meta)
  99. self:send("CAP REQ multi-prefix")
  100. self:invoke("PreRegister", self)
  101. self:send("CAP END")
  102. if password then
  103. self:send("PASS %s", password)
  104. end
  105. self:send("NICK %s", self.nick)
  106. self:send("USER %s 0 * :%s", self.username, self.realname)
  107. self.channels = {}
  108. s:settimeout(0)
  109. repeat
  110. self:think()
  111. socket.select(nil, nil, 0.1) -- Sleep so that we don't eat CPU
  112. until self.authed
  113. end
  114. function meta:disconnect(message)
  115. message = message or "Bye!"
  116. self:invoke("OnDisconnect", message, false)
  117. self:send("QUIT :%s", message)
  118. self:shutdown()
  119. end
  120. function meta:shutdown()
  121. self.socket:close()
  122. setmetatable(self, nil)
  123. end
  124. local function getline(self, errlevel)
  125. local line, err = self.socket:receive("*l")
  126. if not line and err ~= "timeout" and err ~= "wantread" then
  127. self:invoke("OnDisconnect", err, true)
  128. self:shutdown()
  129. error(err, errlevel)
  130. end
  131. return line
  132. end
  133. function meta:think()
  134. while true do
  135. local line = getline(self, 3)
  136. if line and #line > 0 then
  137. if not self:invoke("OnRaw", line) then
  138. self:handle(parse(line))
  139. end
  140. else
  141. break
  142. end
  143. end
  144. end
  145. local handlers = handlers
  146. function meta:handle(prefix, cmd, params)
  147. local handler = handlers[cmd]
  148. if handler then
  149. return handler(self, prefix, unpack(params))
  150. end
  151. end
  152. local whoisHandlers = {
  153. ["311"] = "userinfo";
  154. ["312"] = "node";
  155. ["319"] = "channels";
  156. ["330"] = "account"; -- Freenode
  157. ["307"] = "registered"; -- Unreal
  158. }
  159. function meta:whois(nick)
  160. self:send("WHOIS %s", nick)
  161. local result = {}
  162. while true do
  163. local line = getline(self, 3)
  164. if line then
  165. local prefix, cmd, args = parse(line)
  166. local handler = whoisHandlers[cmd]
  167. if handler then
  168. result[handler] = args
  169. elseif cmd == "318" then
  170. break
  171. else
  172. self:handle(prefix, cmd, args)
  173. end
  174. end
  175. end
  176. if result.account then
  177. result.account = result.account[3]
  178. elseif result.registered then
  179. result.account = result.registered[2]
  180. end
  181. return result
  182. end
  183. function meta:topic(channel)
  184. self:send("TOPIC %s", channel)
  185. end