Lua IRC bot.

init.lua 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. local socket = require "socket"
  2. local serialization = require "serialization"
  3. config = {}
  4. cmds = {}
  5. hooks = {}
  6. timers = {}
  7. function loadconfig()
  8. local f = io.open("./config.lua","rb")
  9. local content = f:read("*a")
  10. f:close()
  11. print(content)
  12. config = serialization.unserialize(content)
  13. hooks = {}
  14. for k,v in pairs(config.hooks) do
  15. print("Loading hook "..v)
  16. local fo=io.open("./hooks/" .. v)
  17. local c = fo:read("*a")
  18. local s,f = pcall(load,c)
  19. fo:close()
  20. if s then
  21. table.insert(hooks,#hooks+1,f)
  22. print("Hook "..v.." loaded")
  23. else
  24. print("Hook "..v.." failed to load:")
  25. print(f)
  26. end
  27. end
  28. cmds = {}
  29. for k,v in pairs(config.cmds) do
  30. local fo=io.open("./cmds/" .. v)
  31. local s,f = pcall(load,fo:read("*a"))
  32. fo:close()
  33. if s then
  34. cmds[k]=f
  35. print("Command "..v.." loaded")
  36. else
  37. print("Command "..v.." failed to load:")
  38. print(f)
  39. end
  40. end
  41. timers = {}
  42. for k,v in pairs(config.timers) do
  43. local fo=io.open("./timers/" .. v)
  44. local s,f = pcall(load,fo:read("*a"))
  45. fo:close()
  46. if s then
  47. table.insert(timers,f)
  48. print("Timer "..v.." loaded")
  49. else
  50. print("Timer "..v.."Failed to load:")
  51. print(f)
  52. end
  53. end
  54. end
  55. function saveconfig()
  56. local newconfig = serialization.serialize(config)
  57. local f = io.open("config.lua","wb")
  58. f:write(newconfig)
  59. f:close()
  60. end
  61. function reply(chan,msg)
  62. end
  63. function os.sleep(n)
  64. os.execute("sleep ".. tostring(tonumber(n)))
  65. end
  66. function checkAdmin(nick)
  67. local pass=false
  68. writeln("WHOIS "..nick)
  69. repeat
  70. line = readln()
  71. print(line)
  72. until line:find("330") ~= nil or line == nil
  73. print ("Line: "..line)
  74. if line == nil then return false end -- wat
  75. local _,e = line:find(nick.." ")
  76. print("Start: "..tostring(e))
  77. local n,_ = line:find(" ",e+1)
  78. print("End: "..tostring(n))
  79. nick = line:sub(e+1,n-1)
  80. print("Logged in as "..nick)
  81. for k,v in pairs(config.admins) do
  82. if nick == v then
  83. print(v .. " = " .. nick)
  84. pass = true
  85. break
  86. else
  87. print(nick .. " != " .. v)
  88. end
  89. end
  90. return pass
  91. end
  92. function addcommand(fname,str)
  93. -- don't use this without pcall
  94. cmds[fname] = load(str)
  95. end
  96. leftHanging = {0,false}
  97. function parsemsg(nick,chan,message)
  98. for k,v in pairs(hooks) do
  99. -- print("Running hook "..k)
  100. local fail,errors = pcall(v,nick,chan,message)
  101. if not fail then print(errors) end
  102. end
  103. if string.find(message,config.prefix) == 1 then
  104. local command = message:sub(2) .. " "
  105. if command == "" then return end
  106. local tCommand = {}
  107. for word in command:gmatch("%S+") do
  108. table.insert(tCommand,word)
  109. end
  110. if tCommand[1]:lower() == "echo" then
  111. local s = command:sub(6)
  112. sendchan(chan,s)
  113. elseif tCommand[1] == "echochan" then
  114. local s = command:sub(11 + tCommand[2]:len())
  115. sendchan(tCommand[2],s)
  116. elseif tCommand[1]:lower() == "action" then
  117. local s = command:sub(7)
  118. sendchan(chan,string.char(1).."ACTION"..s..string.char(1))
  119. elseif tCommand[1] == "actionchan" then
  120. local s = command:sub(string.len("actionchan")+2+ tCommand[2]:len())
  121. sendchan(tCommand[2],string.char(1).."ACTION"..s..string.char(1))
  122. elseif tCommand[1] == "rawecho" then
  123. if checkAdmin(nick) then
  124. writeln(command:sub(9))
  125. end
  126. elseif tCommand[1] == "drop" then
  127. leftHanging = {0, false}
  128. elseif tCommand[1] == "join" then
  129. writeln("JOIN "..tCommand[2])
  130. elseif tCommand[1] == "lua" then
  131. if checkAdmin(nick) then
  132. local s = command:sub(4)
  133. local worked,rval = pcall(load(s))
  134. sendchan(chan,tostring(worked).." "..tostring(rval))
  135. end
  136. elseif tCommand[1] == "whois" then
  137. writeln("WHOIS "..tCommand[2])
  138. elseif tCommand[1] == "addcmd" and checkAdmin(nick) then
  139. --I'll get back to this eventually
  140. elseif tCommand[1] == "quit" then
  141. if checkAdmin(nick) then
  142. sendchan(chan,"Bye! o/")
  143. writeln("QUIT :Blame telstra.")
  144. print("Killed by "..nick)
  145. os.exit(0)
  146. end
  147. elseif tCommand[1] == "restart" then
  148. if checkAdmin(nick) then
  149. sendchan(chan,"Bye! o/")
  150. writeln("QUIT :Blame telstra.")
  151. print("Killed by "..nick)
  152. os.exit(1)
  153. end
  154. elseif tCommand[1] == "debug" then
  155. if checkAdmin(nick) then
  156. config.debug = not config.debug
  157. end
  158. elseif cmds[tCommand[1]] ~= nil then
  159. local fail, errors = pcall(cmds[tCommand[1]],nick,chan,tCommand,message)
  160. if not fail then print(errors) end
  161. end
  162. end
  163. end
  164. function parse(line)
  165. if string.find(line, "PING :") == 1 then
  166. local _,pingid = string.match(line,"([^,]+):([^,]+)")
  167. writeln("PONG :"..pingid)
  168. print("[Ping] "..pingid)
  169. elseif string.find(line,":") == 1 and string.find(line,"PRIVMSG") ~= nil and string.find(line,"005") == nil then
  170. local s = string.sub(line,2) -- I
  171. local ms,me = string.find(s,"!") -- me = match end, ms = match start
  172. local nick = string.sub(s,1,ms-1)
  173. s=string.sub(s,me+1)
  174. local ms,me = string.find(s," PRIVMSG ")
  175. s=string.sub(s,me+1)
  176. local ms,me = string.find(s," :")
  177. local chan = string.sub(s,1,ms-1)
  178. local msg = string.sub(s,me+1)
  179. --[[
  180. -- old parser, kept for hysterical rasins
  181. local nick,s = string.match(s,"([^,]+)!([^\n]+)") -- am
  182. local _,s = string.match(s,"([^,]+) PRIVMSG ([^\n]+)") -- a
  183. local chan, msg = string.match(s,"([^,]+) :([^\n]+)") -- terrible
  184. if chan == config.nick then chan = nick end
  185. ]]--
  186. print("["..chan.."] <"..nick.."> "..msg) --person
  187. parsemsg(nick,chan,msg)
  188. end
  189. end
  190. function main()
  191. print("Loading config.")
  192. loadconfig()
  193. print("Config loaded, resolving "..config.server)
  194. local ip = socket.dns.toip(config.server)
  195. print("Opening connection to "..ip)
  196. local connection = socket.connect(ip,config.port)
  197. function writeln(l) connection:send(l.."\n") end
  198. function sendchan(chan,msg) writeln("PRIVMSG "..chan.." :"..msg) end
  199. function readln() return connection:receive() end
  200. connection:settimeout(1)
  201. print("Connected!")
  202. os.sleep(1)
  203. connection:receive() -- drop a line
  204. print("Logging in.")
  205. connection:send("NICK "..config.nick.."\n")
  206. connection:send("USER "..config.username.." "..config.hostname.." "..config.servername.." "..config.realname.."\n")
  207. repeat
  208. line = connection:receive() or ""
  209. if string.find(line, "PING :") == 1 then
  210. local _,pingid = string.match(line,"([^,]+):([^,]+)")
  211. writeln("PONG :"..pingid)
  212. print("Pinged: "..pingid)
  213. _G.lastping = os.time()
  214. end
  215. if line ~= "" then
  216. print(line)
  217. end
  218. until string.match(line or "","%+i") ~= nil
  219. os.sleep(2)
  220. print("Sent everything relevant. Joining channels.")
  221. if config.autojoin then
  222. for k,v in pairs(config.channels) do
  223. connection:send("JOIN " .. v.."\n")
  224. end
  225. end
  226. _G.lastping = os.time()
  227. repeat
  228. line = connection:receive()
  229. if line ~= nil and line ~= "timeout" then
  230. if config.debug then print(line) end
  231. pcall(parse,line)
  232. else
  233. --[[ if (line == "timeout" or line == nil) and os.time() > _G.lastping + config.timeout then
  234. print(line)
  235. print("Connection to IRC timed out, aborting.")
  236. os.exit(1)
  237. end]]--
  238. end
  239. for k,v in ipairs(timers) do
  240. local fail, errors = pcall(v,line)
  241. if not fail then print(errors) end
  242. end
  243. if line == nil then line = "" end
  244. until string.find(line,"ERROR :Closing link:") ~= nil
  245. print(connection:receive())
  246. end
  247. main()