diff options
author | Elias Fleckenstein <eliasfleckenstein@web.de> | 2021-11-21 19:16:03 +0100 |
---|---|---|
committer | Elias Fleckenstein <eliasfleckenstein@web.de> | 2021-11-21 19:16:03 +0100 |
commit | 783fefdcb375ab58382579afeb092c67b8832813 (patch) | |
tree | fbb3b16efd0b74796c33c2e8754863ea148f2c6c /src | |
parent | 09628cded4b1d5a47b82d0b13eaf1789adb35e91 (diff) | |
download | luairc-783fefdcb375ab58382579afeb092c67b8832813.tar.xz |
Diffstat (limited to 'src')
-rw-r--r-- | src/irc.lua | 239 | ||||
-rw-r--r-- | src/irc/channel.lua | 80 | ||||
-rw-r--r-- | src/irc/constants.lua | 10 | ||||
-rw-r--r-- | src/irc/ctcp.lua | 20 | ||||
-rw-r--r-- | src/irc/dcc.lua | 42 | ||||
-rw-r--r-- | src/irc/debug.lua | 32 | ||||
-rw-r--r-- | src/irc/message.lua | 23 | ||||
-rw-r--r-- | src/irc/misc.lua | 69 |
8 files changed, 262 insertions, 253 deletions
diff --git a/src/irc.lua b/src/irc.lua index a0c22b4..948864e 100644 --- a/src/irc.lua +++ b/src/irc.lua @@ -1,31 +1,46 @@ --- -- Implementation of the main LuaIRC module --- initialization {{{ -local base = _G -local constants = require 'irc.constants' -local ctcp = require 'irc.ctcp' -local c = ctcp._ctcp_quote -local irc_debug = require 'irc.debug' -local message = require 'irc.message' -local misc = require 'irc.misc' -local socket = require 'socket' -local os = require 'os' -local string = require 'string' -local table = require 'table' --- }}} - --- -- LuaIRC - IRC framework written in Lua -- @release 0.3 -module 'irc' +local irc = {} -- constants {{{ -_VERSION = 'LuaIRC 0.3' +irc._VERSION = 'LuaIRC 0.3 (Lua 5.3 Port)' +-- }}} + +-- libraries {{{ +local libs = {} + +libs.irc = irc +libs.socket = require 'socket' + +local old_libs = _G.libs +_G.libs = libs + +libs.constants = require 'irc.constants' +libs.ctcp = require 'irc.ctcp' +libs.debug = require 'irc.debug' +libs.misc = require 'irc.misc' +libs.channel = require 'irc.channel' +libs.dcc = require 'irc.dcc' +libs.message = require 'irc.message' + +_G.libs = old_libs + +-- localize modules {{{ +local constants = libs.constants +local ctcp = libs.ctcp +local c = ctcp._ctcp_quote +local irc_debug = libs.debug +local message = libs.message +local misc = libs.misc +local socket = libs.socket -- }}} -- classes {{{ -local Channel = base.require 'irc.channel' +local Channel = libs.channel -- }}} -- local variables {{{ @@ -51,14 +66,14 @@ local ip = nil -- }}} -- defaults {{{ -TIMEOUT = 60 -- connection timeout -NETWORK = "localhost" -- default network -PORT = 6667 -- default port -NICK = "luabot" -- default nick -USERNAME = "LuaIRC" -- default username -REALNAME = "LuaIRC" -- default realname -DEBUG = false -- whether we want extra debug information -OUTFILE = nil -- file to send debug output to - nil is stdout +irc.TIMEOUT = 60 -- connection timeout +irc.NETWORK = "localhost" -- default network +irc.PORT = 6667 -- default port +irc.NICK = "luabot" -- default nick +irc.USERNAME = "LuaIRC" -- default username +irc.REALNAME = "LuaIRC" -- default realname +irc.DEBUG = false -- whether we want extra debug information +irc.OUTFILE = nil -- file to send debug output to - nil is stdout -- }}} -- private functions {{{ @@ -68,21 +83,21 @@ local function main_loop_iter() local rready, wready, err = socket.select(rsockets, wsockets) if err then irc_debug._err(err); return false; end - for _, sock in base.ipairs(rready) do + for _, sock in ipairs(rready) do local cb = socket.protect(rcallbacks[sock]) local ret, err = cb(sock) if not ret then irc_debug._warn("socket error: " .. err) - _unregister_socket(sock, 'r') + irc._unregister_socket(sock, 'r') end end - for _, sock in base.ipairs(wready) do + for _, sock in ipairs(wready) do local cb = socket.protect(wcallbacks[sock]) local ret, err = cb(sock) if not ret then irc_debug._warn("socket error: " .. err) - _unregister_socket(sock, 'w') + irc._unregister_socket(sock, 'w') end end @@ -103,7 +118,7 @@ local function incoming_message(sock) local msg = message._parse(raw_msg) misc._try_call_warn("Unhandled server message: " .. msg.command, handlers["on_" .. msg.command:lower()], - (misc._parse_user(msg.from)), base.unpack(msg.args)) + (misc._parse_user(msg.from)), table.unpack(msg.args)) return true end -- }}} @@ -119,7 +134,7 @@ end -- command handlers {{{ -- on_nick {{{ function handlers.on_nick(from, new_nick) - for chan in channels() do + for chan in irc.channels() do chan:_change_nick(from, new_nick) end callback("nick_change", new_nick, from) @@ -128,7 +143,7 @@ end -- on_join {{{ function handlers.on_join(from, chan) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received join message for unknown channel: " .. chan) if serverinfo.channels[chan].join_complete then serverinfo.channels[chan]:_add_user(from) @@ -157,7 +172,7 @@ function handlers.on_mode(from, to, mode_string, ...) if to:sub(1, 1) == "#" then -- handle channel mode requests {{{ - base.assert(serverinfo.channels[to], + assert(serverinfo.channels[to], "Received mode change for unknown channel: " .. to) local chan = serverinfo.channels[to] local ind = 1 @@ -204,7 +219,7 @@ end -- on_topic {{{ function handlers.on_topic(from, chan, new_topic) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received topic message for unknown channel: " .. chan) serverinfo.channels[chan]._topic.text = new_topic serverinfo.channels[chan]._topic.user = from @@ -223,7 +238,7 @@ end -- on_kick {{{ function handlers.on_kick(from, chan, to) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received kick message for unknown channel: " .. chan) if serverinfo.channels[chan].join_complete then serverinfo.channels[chan]:_remove_user(to) @@ -235,7 +250,7 @@ end -- on_privmsg {{{ function handlers.on_privmsg(from, to, msg) local msgs = ctcp._ctcp_split(msg) - for _, v in base.ipairs(msgs) do + for _, v in ipairs(msgs) do local msg = v.str if v.ctcp then -- ctcp message {{{ @@ -245,16 +260,16 @@ function handlers.on_privmsg(from, to, msg) table.remove(words, 1) -- not using try_call here because the ctcp specification requires -- an error response to nonexistant commands - if base.type(ctcp_handlers[cb]) == "function" then + if type(ctcp_handlers[cb]) == "function" then ctcp_handlers[cb](from, to, table.concat(words, " ")) else - notice(from, c("ERRMSG", received_command, ":Unknown query")) + irc.notice(from, c("ERRMSG", received_command, ":Unknown query")) end -- }}} else -- normal message {{{ if to:sub(1, 1) == "#" then - base.assert(serverinfo.channels[to], + assert(serverinfo.channels[to], "Received channel msg from unknown channel: " .. to) callback("channel_msg", serverinfo.channels[to], from, msg) else @@ -269,7 +284,7 @@ end -- on_notice {{{ function handlers.on_notice(from, to, msg) local msgs = ctcp._ctcp_split(msg) - for _, v in base.ipairs(msgs) do + for _, v in ipairs(msgs) do local msg = v.str if v.ctcp then -- ctcp message {{{ @@ -283,7 +298,7 @@ function handlers.on_notice(from, to, msg) else -- normal message {{{ if to:sub(1, 1) == "#" then - base.assert(serverinfo.channels[to], + assert(serverinfo.channels[to], "Received channel msg from unknown channel: " .. to) callback("channel_notice", serverinfo.channels[to], from, msg) else @@ -297,7 +312,7 @@ end -- on_quit {{{ function handlers.on_quit(from, quit_msg) - for name, chan in base.pairs(serverinfo.channels) do + for name, chan in pairs(serverinfo.channels) do chan:_remove_user(from) end callback("quit", from, quit_msg) @@ -307,7 +322,7 @@ end -- on_ping {{{ -- respond to server pings to make sure it knows we are alive function handlers.on_ping(from, respond_to) - send("PONG", respond_to) + irc.send("PONG", respond_to) end -- }}} -- }}} @@ -316,7 +331,7 @@ end -- on_rpl_topic {{{ -- catch topic changes function handlers.on_rpl_topic(from, chan, topic) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received topic information about unknown channel: " .. chan) serverinfo.channels[chan]._topic.text = topic end @@ -324,7 +339,7 @@ end -- on_rpl_notopic {{{ function handlers.on_rpl_notopic(from, chan) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received topic information about unknown channel: " .. chan) serverinfo.channels[chan]._topic.text = "" end @@ -333,21 +348,21 @@ end -- on_rpl_topicdate {{{ -- "topic was set by <user> at <time>" function handlers.on_rpl_topicdate(from, chan, user, time) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received topic information about unknown channel: " .. chan) serverinfo.channels[chan]._topic.user = user - serverinfo.channels[chan]._topic.time = base.tonumber(time) + serverinfo.channels[chan]._topic.time = tonumber(time) end -- }}} -- on_rpl_namreply {{{ -- handles a NAMES reply function handlers.on_rpl_namreply(from, chanmode, chan, userlist) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received user information about unknown channel: " .. chan) serverinfo.channels[chan]._chanmode = constants.chanmodes[chanmode] local users = misc._split(userlist) - for k,v in base.ipairs(users) do + for k,v in ipairs(users) do if v:sub(1, 1) == "@" or v:sub(1, 1) == "+" then local nick = v:sub(2) serverinfo.channels[chan]:_add_user(nick, v:sub(1, 1)) @@ -362,7 +377,7 @@ end -- when we get this message, the channel join has completed, so call the -- external cb function handlers.on_rpl_endofnames(from, chan) - base.assert(serverinfo.channels[chan], + assert(serverinfo.channels[chan], "Received user information about unknown channel: " .. chan) if not serverinfo.channels[chan].join_complete then callback("me_join", serverinfo.channels[chan]) @@ -425,7 +440,7 @@ function handlers.on_rpl_whoischannels(from, nick, channel_list) if not requestinfo.whois[nick].channels then requestinfo.whois[nick].channels = {} end - for _, channel in base.ipairs(misc._split(channel_list)) do + for _, channel in ipairs(misc._split(channel_list)) do table.insert(requestinfo.whois[nick].channels, channel) end end @@ -466,7 +481,7 @@ function handlers.on_rpl_endofwhois(from, nick) local cb = table.remove(icallbacks.whois[nick], 1) cb(requestinfo.whois[nick]) requestinfo.whois[nick] = nil - if #icallbacks.whois[nick] > 0 then send("WHOIS", nick) + if #icallbacks.whois[nick] > 0 then irc.send("WHOIS", nick) else icallbacks.whois[nick] = nil end end @@ -476,7 +491,7 @@ end function handlers.on_rpl_version(from, version, server, comments) local cb = table.remove(icallbacks.serverversion[server], 1) cb({version = version, server = server, comments = comments}) - if #icallbacks.serverversion[server] > 0 then send("VERSION", server) + if #icallbacks.serverversion[server] > 0 then irc.send("VERSION", server) else icallbacks.serverversion[server] = nil end end @@ -486,7 +501,7 @@ end function on_rpl_time(from, server, time) local cb = table.remove(icallbacks.servertime[server], 1) cb({time = time, server = server}) - if #icallbacks.servertime[server] > 0 then send("TIME", server) + if #icallbacks.servertime[server] > 0 then irc.send("TIME", server) else icallbacks.servertime[server] = nil end end @@ -498,7 +513,7 @@ end -- on_action {{{ function ctcp_handlers.on_action(from, to, message) if to:sub(1, 1) == "#" then - base.assert(serverinfo.channels[to], + assert(serverinfo.channels[to], "Received channel msg from unknown channel: " .. to) callback("channel_act", serverinfo.channels[to], from, message) else @@ -511,7 +526,7 @@ end -- TODO: can we not have this handler be registered unless the dcc module is -- loaded? function ctcp_handlers.on_dcc(from, to, message) - local type, argument, address, port, size = base.unpack(misc._split(message, " ", nil, '"', '"')) + local type, argument, address, port, size = table.unpack(misc._split(message, " ", nil, '"', '"')) address = misc._ip_int_to_str(address) if type == "SEND" then if callback("dcc_send", from, to, argument, address, port, size) then @@ -525,25 +540,25 @@ end -- on_version {{{ function ctcp_handlers.on_version(from, to) - notice(from, c("VERSION", _VERSION .. " running under " .. base._VERSION .. " with " .. socket._VERSION)) + irc.notice(from, c("VERSION", irc._VERSION .. " running under " .. _VERSION .. " with " .. socket._VERSION)) end -- }}} -- on_errmsg {{{ function ctcp_handlers.on_errmsg(from, to, message) - notice(from, c("ERRMSG", message, ":No error has occurred")) + irc.notice(from, c("ERRMSG", message, ":No error has occurred")) end -- }}} -- on_ping {{{ function ctcp_handlers.on_ping(from, to, timestamp) - notice(from, c("PING", timestamp)) + irc.notice(from, c("PING", timestamp)) end -- }}} -- on_time {{{ function ctcp_handlers.on_time(from, to) - notice(from, c("TIME", os.date())) + irc.notice(from, c("TIME", os.date())) end -- }}} -- }}} @@ -559,7 +574,7 @@ function ctcp_handlers.on_rpl_version(from, to, version) local lfrom = from:lower() local cb = table.remove(icallbacks.ctcp_version[lfrom], 1) cb({version = version, nick = from}) - if #icallbacks.ctcp_version[lfrom] > 0 then say(from, c("VERSION")) + if #icallbacks.ctcp_version[lfrom] > 0 then irc.say(from, c("VERSION")) else icallbacks.ctcp_version[lfrom] = nil end end @@ -576,7 +591,7 @@ function ctcp_handlers.on_rpl_ping(from, to, timestamp) local lfrom = from:lower() local cb = table.remove(icallbacks.ctcp_ping[lfrom], 1) cb({time = os.time() - timestamp, nick = from}) - if #icallbacks.ctcp_ping[lfrom] > 0 then say(from, c("PING", os.time())) + if #icallbacks.ctcp_ping[lfrom] > 0 then irc.say(from, c("PING", os.time())) else icallbacks.ctcp_ping[lfrom] = nil end end @@ -587,7 +602,7 @@ function ctcp_handlers.on_rpl_time(from, to, time) local lfrom = from:lower() local cb = table.remove(icallbacks.ctcp_time[lfrom], 1) cb({time = time, nick = from}) - if #icallbacks.ctcp_time[lfrom] > 0 then say(from, c("TIME")) + if #icallbacks.ctcp_time[lfrom] > 0 then irc.say(from, c("TIME")) else icallbacks.ctcp_time[lfrom] = nil end end @@ -605,7 +620,7 @@ end -- @param mode 'r' if the socket is for reading, 'w' if for writing -- @param cb Callback to call when the socket is ready for reading/writing. -- It will be called with the socket as the single argument. -function _register_socket(sock, mode, cb) +function irc._register_socket(sock, mode, cb) local socks, cbs if mode == 'r' then socks = rsockets @@ -614,7 +629,7 @@ function _register_socket(sock, mode, cb) socks = wsockets cbs = wcallbacks end - base.assert(not cbs[sock], "socket already registered") + assert(not cbs[sock], "socket already registered") table.insert(socks, sock) cbs[sock] = cb end @@ -625,7 +640,7 @@ end -- Remove a previously registered socket. -- @param sock Socket to unregister -- @param mode 'r' to unregister it for reading, 'w' for writing -function _unregister_socket(sock, mode) +function irc._unregister_socket(sock, mode) local socks, cbs if mode == 'r' then socks = rsockets @@ -634,7 +649,7 @@ function _unregister_socket(sock, mode) socks = wsockets cbs = wcallbacks end - for i, v in base.ipairs(socks) do + for i, v in ipairs(socks) do if v == sock then table.remove(socks, i); break; end end cbs[sock] = nil @@ -670,22 +685,22 @@ end -- dropping an idle connection -- (default: '60')</li> -- </ul> -function connect(args) - local network = args.network or NETWORK - local port = args.port or PORT - local nick = args.nick or NICK - local username = args.username or USERNAME - local realname = args.realname or REALNAME - local timeout = args.timeout or TIMEOUT +function irc.connect(args) + local network = args.network or irc.NETWORK + local port = args.port or irc.PORT + local nick = args.nick or irc.NICK + local username = args.username or irc.USERNAME + local realname = args.realname or irc.REALNAME + local timeout = args.timeout or irc.TIMEOUT serverinfo.connecting = true - if OUTFILE then irc_debug.set_output(OUTFILE) end - if DEBUG then irc_debug.enable() end - irc_sock = base.assert(socket.connect(network, port)) + if irc.OUTFILE then irc_debug.set_output(irc.OUTFILE) end + if irc.DEBUG then irc_debug.enable() end + irc_sock = assert(socket.connect(network, port)) irc_sock:settimeout(timeout) - _register_socket(irc_sock, 'r', incoming_message) - if args.pass then send("PASS", args.pass) end - send("NICK", nick) - send("USER", username, get_ip(), network, realname) + irc._register_socket(irc_sock, 'r', incoming_message) + if args.pass then irc.send("PASS", args.pass) end + irc.send("NICK", nick) + irc.send("USER", username, irc.get_ip(), network, realname) begin_main_loop() end -- }}} @@ -694,9 +709,9 @@ end --- -- Close the connection to the irc server. -- @param message Quit message (optional, defaults to 'Leaving') -function quit(message) +function irc.quit(message) message = message or "Leaving" - send("QUIT", message) + irc.send("QUIT", message) serverinfo.connected = false end -- }}} @@ -705,10 +720,10 @@ end --- -- Join a channel. -- @param channel Channel to join -function join(channel) +function irc.join(channel) if not channel then return end serverinfo.channels[channel] = Channel.new(channel) - send("JOIN", channel) + irc.send("JOIN", channel) end -- }}} @@ -716,10 +731,10 @@ end --- -- Leave a channel. -- @param channel Channel to leave -function part(channel) +function irc.part(channel) if not channel then return end serverinfo.channels[channel] = nil - send("PART", channel) + irc.send("PART", channel) end -- }}} @@ -728,10 +743,10 @@ end -- Send a message to a user or channel. -- @param name User or channel to send the message to -- @param message Message to send -function say(name, message) +function irc.say(name, message) if not name then return end message = message or "" - send("PRIVMSG", name, message) + irc.send("PRIVMSG", name, message) end -- }}} @@ -740,10 +755,10 @@ end -- Send a notice to a user or channel. -- @param name User or channel to send the notice to -- @param message Message to send -function notice(name, message) +function irc.notice(name, message) if not name then return end message = message or "" - send("NOTICE", name, message) + irc.send("NOTICE", name, message) end -- }}} @@ -752,10 +767,10 @@ end -- Perform a /me action. -- @param name User or channel to send the action to -- @param action Action to send -function act(name, action) +function irc.act(name, action) if not name then return end action = action or "" - send("PRIVMSG", name, c("ACTION", action)) + irc.send("PRIVMSG", name, c("ACTION", action)) end -- }}} -- }}} @@ -771,13 +786,13 @@ end -- <li><i>version:</i> the server version</li> -- <li><i>comments:</i> other data provided by the server</li> -- </ul> -function server_version(cb) +function irc.server_version(cb) -- apparently the optional server parameter isn't supported for servers -- which you are not directly connected to (freenode specific?) local server = serverinfo.host if not icallbacks.serverversion[server] then icallbacks.serverversion[server] = {cb} - send("VERSION", server) + irc.send("VERSION", server) else table.insert(icallbacks.serverversion[server], cb) end @@ -808,12 +823,12 @@ end -- joined</li> -- </ul> -- @param nick User to request WHOIS information about -function whois(cb, nick) +function irc.whois(cb, nick) nick = nick:lower() requestinfo.whois[nick] = {} if not icallbacks.whois[nick] then icallbacks.whois[nick] = {cb} - send("WHOIS", nick) + irc.send("WHOIS", nick) else table.insert(icallbacks.whois[nick], cb) end @@ -829,13 +844,13 @@ end -- <li><i>server:</i> the server which responded to the request</li> -- <li><i>time:</i> the time reported by the server</li> -- </ul> -function server_time(cb) +function irc.server_time(cb) -- apparently the optional server parameter isn't supported for servers -- which you are not directly connected to (freenode specific?) local server = serverinfo.host if not icallbacks.servertime[server] then icallbacks.servertime[server] = {cb} - send("TIME", server) + irc.send("TIME", server) else table.insert(icallbacks.servertime[server], cb) end @@ -854,11 +869,11 @@ end -- <li><i>time:</i> the roundtrip ping time, in seconds</li> -- </ul> -- @param nick User to ping -function ctcp_ping(cb, nick) +function irc.ctcp_ping(cb, nick) nick = nick:lower() if not icallbacks.ctcp_ping[nick] then icallbacks.ctcp_ping[nick] = {cb} - say(nick, c("PING", os.time())) + irc.say(nick, c("PING", os.time())) else table.insert(icallbacks.ctcp_ping[nick], cb) end @@ -875,11 +890,11 @@ end -- <li><i>time:</i> the localtime reported by the remote client</li> -- </ul> -- @param nick User to request the localtime from -function ctcp_time(cb, nick) +function irc.ctcp_time(cb, nick) nick = nick:lower() if not icallbacks.ctcp_time[nick] then icallbacks.ctcp_time[nick] = {cb} - say(nick, c("TIME")) + irc.say(nick, c("TIME")) else table.insert(icallbacks.ctcp_time[nick], cb) end @@ -896,11 +911,11 @@ end -- <li><i>version:</i> the version reported by the remote client</li> -- </ul> -- @param nick User to request the client version from -function ctcp_version(cb, nick) +function irc.ctcp_version(cb, nick) nick = nick:lower() if not icallbacks.ctcp_version[nick] then icallbacks.ctcp_version[nick] = {cb} - say(nick, c("VERSION")) + irc.say(nick, c("VERSION")) else table.insert(icallbacks.ctcp_version[nick], cb) end @@ -917,7 +932,7 @@ end -- callback for this event -- @return Value of the original callback for this event (or nil if no previous -- callback had been set) -function register_callback(name, fn) +function irc.register_callback(name, fn) local old_handler = user_handlers[name] user_handlers[name] = fn return old_handler @@ -936,10 +951,10 @@ end -- an array. Strings are sent literally, arrays are CTCP quoted -- as a group. The last argument (if it exists) is preceded by -- a : (so it may contain spaces). -function send(command, ...) +function irc.send(command, ...) if not serverinfo.connected and not serverinfo.connecting then return end local message = command - for i, v in base.ipairs({...}) do + for i, v in ipairs({...}) do if i == #{...} then v = ":" .. v end @@ -958,7 +973,7 @@ end -- Get the local IP address for the server connection. -- @return A string representation of the local IP address that the IRC server -- connection is communicating on -function get_ip() +function irc.get_ip() return (ip or irc_sock:getsockname()) end -- }}} @@ -967,7 +982,7 @@ end --- -- Set the local IP manually (to allow for NAT workarounds) -- @param new_ip IP address to set -function set_ip(new_ip) +function irc.set_ip(new_ip) ip = new_ip end -- }}} @@ -979,7 +994,7 @@ end -- channels() is an iterator function for use in for loops. -- For example, <pre>for chan in irc.channels() do print(chan:name) end</pre> -- @see irc.channel -function channels() +function irc.channels() return function(state, arg) return misc._value_iter(state, arg, function(v) @@ -992,3 +1007,5 @@ end -- }}} -- }}} -- }}} + +return irc diff --git a/src/irc/channel.lua b/src/irc/channel.lua index 82bca71..64011a6 100644 --- a/src/irc/channel.lua +++ b/src/irc/channel.lua @@ -2,17 +2,15 @@ -- Implementation of the Channel class -- initialization {{{ -local base = _G -local irc = require 'irc' -local misc = require 'irc.misc' -local socket = require 'socket' -local table = require 'table' +local irc = libs.irc +local misc = libs.misc +local socket = libs.socket -- }}} --- -- This module implements a channel object representing a single channel we -- have joined. -module 'irc.channel' +local channel = {} -- object metatable {{{ -- TODO: this <br /> shouldn't be necessary - bug in luadoc @@ -37,7 +35,7 @@ local mt = { elseif key == "chanmode" then return self._chanmode else - return _M[key] + return channel[key] end end, -- }}} @@ -50,7 +48,7 @@ local mt = { elseif key == "chanmode" then return else - base.rawset(self, key, value) + rawset(self, key, value) end end, -- }}} @@ -58,12 +56,12 @@ local mt = { __concat = function(first, second) local first_str, second_str - if base.type(first) == "table" then + if type(first) == "table" then first_str = first._name else first_str = first end - if base.type(second) == "table" then + if type(second) == "table" then second_str = second._name else second_str = second @@ -88,7 +86,7 @@ local mt = { -- @param self Channel object -- @param set True to set the mode, false to unset it -- @param letter Letter of the mode -local function set_basic_mode(self, set, letter) +local function set_basic_mode(set, letter) if set then irc.send("MODE", self.name, "+" .. letter) else @@ -107,7 +105,7 @@ end -- @param self Channel object -- @param user Nick of the user to add -- @param mode Mode (op/voice) of the user, in symbolic form (@/+) -function _add_user(self, user, mode) +function channel:_add_user(user, mode) mode = mode or '' self._members[user] = mode .. user end @@ -118,7 +116,7 @@ end -- Remove a user from the channel's internal user list. -- @param self Channel object -- @param user Nick of the user to remove -function _remove_user(self, user) +function channel:_remove_user(user) self._members[user] = nil end -- }}} @@ -130,7 +128,7 @@ end -- @param user Nick of the user to affect -- @param on True if the mode is being set, false if it's being unset -- @param mode 'o' for op, 'v' for voice -function _change_status(self, user, on, mode) +function channel:_change_status(user, on, mode) if on then if mode == 'o' then self._members[user] = '@' .. user @@ -152,7 +150,7 @@ end -- @param self Channel object -- @param old_nick User's old nick -- @param new_nick User's new nick -function _change_nick(self, old_nick, new_nick) +function channel:_change_nick(old_nick, new_nick) for member in self:each_member() do local member_nick = member:gsub('@+', '') if member_nick == old_nick then @@ -172,8 +170,8 @@ end -- Creates a new Channel object. -- @param chan Name of the new channel -- @return The new channel instance -function new(chan) - return base.setmetatable({_name = chan, _topic = {}, _chanmode = "", +function channel.new(chan) + return setmetatable({_name = chan, _topic = {}, _chanmode = "", _members = {}}, mt) end -- }}} @@ -184,7 +182,7 @@ end --- -- Iterator over the ops in the channel -- @param self Channel object -function each_op(self) +function channel:each_op() return function(state, arg) return misc._value_iter(state, arg, function(v) @@ -200,7 +198,7 @@ end --- -- Iterator over the voiced users in the channel -- @param self Channel object -function each_voice(self) +function channel:each_voice() return function(state, arg) return misc._value_iter(state, arg, function(v) @@ -216,7 +214,7 @@ end --- -- Iterator over the normal users in the channel -- @param self Channel object -function each_user(self) +function channel:each_user() return function(state, arg) return misc._value_iter(state, arg, function(v) @@ -233,7 +231,7 @@ end --- -- Iterator over all users in the channel -- @param self Channel object -function each_member(self) +function channel:each_member() return misc._value_iter, self._members, nil end -- }}} @@ -245,7 +243,7 @@ end -- Gets an array of all the ops in the channel. -- @param self Channel object -- @return Array of channel ops -function ops(self) +function channel:ops() local ret = {} for nick in self:each_op() do table.insert(ret, nick) @@ -259,7 +257,7 @@ end -- Gets an array of all the voiced users in the channel. -- @param self Channel object -- @return Array of channel voiced users -function voices(self) +function channel:voices() local ret = {} for nick in self:each_voice() do table.insert(ret, nick) @@ -273,7 +271,7 @@ end -- Gets an array of all the normal users in the channel. -- @param self Channel object -- @return Array of channel normal users -function users(self) +function channel:users() local ret = {} for nick in self:each_user() do table.insert(ret, nick) @@ -287,7 +285,7 @@ end -- Gets an array of all the users in the channel. -- @param self Channel object -- @return Array of channel users -function members(self) +function channel:members() local ret = {} -- not just returning self._members, since the return value shouldn't be -- modifiable @@ -306,7 +304,7 @@ end -- Ban a user from a channel. -- @param self Channel object -- @param name User to ban -function ban(self, name) +function channel:ban(name) irc.send("MODE", self.name, "+b", name) end -- }}} @@ -317,7 +315,7 @@ end -- Remove a ban on a user. -- @param self Channel object -- @param name User to unban -function unban(self, name) +function channel:unban(name) irc.send("MODE", self.name, "-b", name) end -- }}} @@ -327,7 +325,7 @@ end -- Give a user voice on a channel. -- @param self Channel object -- @param name User to give voice to -function voice(self, name) +function channel:voice(name) irc.send("MODE", self.name, "+v", name) end -- }}} @@ -337,7 +335,7 @@ end -- Remove voice from a user. -- @param self Channel object -- @param name User to remove voice from -function devoice(self, name) +function channel:devoice(name) irc.send("MODE", self.name, "-v", name) end -- }}} @@ -347,7 +345,7 @@ end -- Give a user ops on a channel. -- @param self Channel object -- @param name User to op -function op(self, name) +function channel:op(name) irc.send("MODE", self.name, "+o", name) end -- }}} @@ -357,7 +355,7 @@ end -- Remove ops from a user. -- @param self Channel object -- @param name User to remove ops from -function deop(self, name) +function channel:deop(name) irc.send("MODE", self.name, "-o", name) end -- }}} @@ -368,7 +366,7 @@ end -- @param self Channel object -- @param new_limit New value for the channel limit (optional; limit is unset -- if this argument isn't passed) -function set_limit(self, new_limit) +function channel:set_limit(new_limit) if new_limit then irc.send("MODE", self.name, "+l", new_limit) else @@ -383,7 +381,7 @@ end -- @param self Channel object -- @param key New channel password (optional; password is unset if this -- argument isn't passed) -function set_key(self, key) +function channel:set_key(key) if key then irc.send("MODE", self.name, "+k", key) else @@ -397,7 +395,7 @@ end -- Set the private state of a channel. -- @param self Channel object -- @param set True to set the channel as private, false to unset it -function set_private(self, set) +function channel:set_private(set) set_basic_mode(self, set, "p") end -- }}} @@ -407,7 +405,7 @@ end -- Set the secret state of a channel. -- @param self Channel object -- @param set True to set the channel as secret, false to unset it -function set_secret(self, set) +function channel:set_secret(set) set_basic_mode(self, set, "s") end -- }}} @@ -417,7 +415,7 @@ end -- Set whether joining the channel requires an invite. -- @param self Channel object -- @param set True to set the channel invite only, false to unset it -function set_invite_only(self, set) +function channel:set_invite_only(set) set_basic_mode(self, set, "i") end -- }}} @@ -427,7 +425,7 @@ end -- If true, the topic can only be changed by an op. -- @param self Channel object -- @param set True to lock the topic, false to unlock it -function set_topic_lock(self, set) +function channel:set_topic_lock(set) set_basic_mode(self, set, "t") end -- }}} @@ -438,7 +436,7 @@ end -- @param self Channel object -- @param set True to require users to be in the channel to send messages to -- it, false to remove this restriction -function set_no_outside_messages(self, set) +function channel:set_no_outside_messages(set) set_basic_mode(self, set, "n") end -- }}} @@ -448,7 +446,7 @@ end -- Set whether voice is required to speak. -- @param self Channel object -- @param set True to set the channel as moderated, false to unset it -function set_moderated(self, set) +function channel:set_moderated(set) set_basic_mode(self, set, "m") end -- }}} @@ -461,7 +459,7 @@ end -- @param self Channel object -- @param nick Nick to search for -- @return True if the nick is in the channel, false otherwise -function contains(self, nick) +function channel:contains(nick) for member in self:each_member() do local member_nick = member:gsub('@+', '') if member_nick == nick then @@ -473,3 +471,5 @@ end -- }}} -- }}} -- }}} + +return channel diff --git a/src/irc/constants.lua b/src/irc/constants.lua index b26cecb..dfee506 100644 --- a/src/irc/constants.lua +++ b/src/irc/constants.lua @@ -1,13 +1,13 @@ --- -- This module holds various constants used by the IRC protocol. -module "irc.constants" +local constants = {} -- protocol constants {{{ -IRC_MAX_MSG = 512 +constants.IRC_MAX_MSG = 512 -- }}} -- server replies {{{ -replies = { +constants.replies = { -- Command responses {{{ [001] = "RPL_WELCOME", [002] = "RPL_YOURHOST", @@ -183,9 +183,11 @@ replies = { -- }}} -- chanmodes {{{ -chanmodes = { +constants.chanmodes = { ["@"] = "secret", ["*"] = "private", ["="] = "public" } -- }}} + +return constants diff --git a/src/irc/ctcp.lua b/src/irc/ctcp.lua index c77cae8..c229064 100644 --- a/src/irc/ctcp.lua +++ b/src/irc/ctcp.lua @@ -1,14 +1,10 @@ --- -- Implementation of the CTCP protocol --- initialization {{{ -local base = _G -local table = require "table" --- }}} --- -- This module implements the various quoting and escaping requirements of the -- CTCP protocol. -module "irc.ctcp" +local ctcp = {} -- internal functions {{{ -- _low_quote {{{ @@ -17,7 +13,7 @@ module "irc.ctcp" -- to appear in an IRC packet). -- @param ... Strings to quote together, space separated -- @return Quoted string -function _low_quote(...) +function ctcp._low_quote(...) local str = table.concat({...}, " ") return str:gsub("[%z\n\r\020]", {["\000"] = "\0200", ["\n"] = "\020n", @@ -31,7 +27,7 @@ end -- Removes low level quoting done by low_quote. -- @param str String with low level quoting applied to it -- @return String with those quoting methods stripped off -function _low_dequote(str) +function ctcp._low_dequote(str) return str:gsub("\020(.?)", function(s) if s == "0" then return "\000" end if s == "n" then return "\n" end @@ -48,7 +44,7 @@ end -- data (by the calling program). -- @param ... Strings to apply CTCP quoting to together, space separated -- @return String with CTCP quoting applied -function _ctcp_quote(...) +function ctcp._ctcp_quote(...) local str = table.concat({...}, " ") local ret = str:gsub("[\001\\]", {["\001"] = "\\a", ["\\"] = "\\\\"}) @@ -62,7 +58,7 @@ end -- data (likely by ctcp_split). -- @param str String with CTCP quoting -- @return String with all CTCP quoting stripped -function _ctcp_dequote(str) +function ctcp._ctcp_dequote(str) local ret = str:gsub("^\001", ""):gsub("\001$", "") return ret:gsub("\\(.?)", function(s) if s == "a" then return "\001" end @@ -84,7 +80,7 @@ end -- <li><i>ctcp:</i> True if the section was a CTCP message, false -- otherwise</li> -- </ul> -function _ctcp_split(str) +function ctcp._ctcp_split(str) local ret = {} local iter = 1 while true do @@ -103,7 +99,7 @@ function _ctcp_split(str) end if not s then break end if ctcp_string ~= "" then - table.insert(ret, {str = _ctcp_dequote(ctcp_string), ctcp = true}) + table.insert(ret, {str = ctcp._ctcp_dequote(ctcp_string), ctcp = true}) end iter = e + 1 @@ -113,3 +109,5 @@ function _ctcp_split(str) end -- }}} -- }}} + +return ctcp diff --git a/src/irc/dcc.lua b/src/irc/dcc.lua index 6e8537e..6d9b347 100644 --- a/src/irc/dcc.lua +++ b/src/irc/dcc.lua @@ -1,26 +1,22 @@ --- -- Implementation of the DCC protocol -- initialization {{{ -local base = _G -local irc = require 'irc' -local ctcp = require 'irc.ctcp' +local irc = libs.irc +local ctcp = libs.ctcp local c = ctcp._ctcp_quote -local irc_debug = require 'irc.debug' -local misc = require 'irc.misc' -local socket = require 'socket' -local coroutine = require 'coroutine' -local io = require 'io' -local string = require 'string' +local irc_debug = libs.debug +local misc = libs.misc +local socket = libs.socket -- }}} --- -- This module implements the DCC protocol. File transfers (DCC SEND) are -- handled, but DCC CHAT is not, as of yet. -module 'irc.dcc' +local dcc = {} -- defaults {{{ -FIRST_PORT = 1028 -LAST_PORT = 5000 +dcc.FIRST_PORT = 1028 +dcc.LAST_PORT = 5000 -- }}} -- private functions {{{ @@ -138,13 +134,13 @@ end -- @param address IP address of the remote user in low level int form -- @param port Port to connect to at the remote user -- @param packet_size Size of the packets the remote user will be sending -function _accept(filename, address, port, packet_size) +function dcc._accept(filename, address, port, packet_size) debug_dcc("Accepting a DCC SEND request from " .. address .. ":" .. port) packet_size = packet_size or 1024 - local sock = base.assert(socket.tcp()) - base.assert(sock:connect(address, port)) + local sock = assert(socket.tcp()) + assert(sock:connect(address, port)) sock:settimeout(0.1) - local file = base.assert(io.open(misc._get_unique_filename(filename), "w")) + local file = assert(io.open(misc._get_unique_filename(filename), "w")) irc._register_socket(sock, 'r', coroutine.wrap(function(s) return accept_file(s, file, packet_size) @@ -162,17 +158,17 @@ end -- @param port Port to accept connections on (optional, defaults to -- choosing an available port between FIRST_PORT and LAST_PORT -- above) -function send(nick, filename, port) - port = port or FIRST_PORT +function dcc.send(nick, filename, port) + port = port or dcc.FIRST_PORT local sock repeat - sock = base.assert(socket.tcp()) + sock = assert(socket.tcp()) err, msg = sock:bind('*', port) port = port + 1 - until msg ~= "address already in use" and port <= LAST_PORT + 1 + until msg ~= "address already in use" and port <= dcc.LAST_PORT + 1 port = port - 1 - base.assert(err, msg) - base.assert(sock:listen(1)) + assert(err, msg) + assert(sock:listen(1)) local ip = misc._ip_str_to_int(irc.get_ip()) local file, err = io.open(filename) if not file then @@ -194,3 +190,5 @@ function send(nick, filename, port) end -- }}} -- }}} + +return dcc diff --git a/src/irc/debug.lua b/src/irc/debug.lua index 414b49d..5bfbee6 100644 --- a/src/irc/debug.lua +++ b/src/irc/debug.lua @@ -1,17 +1,13 @@ --- -- Basic debug output --- initialization {{{ -local base = _G -local io = require 'io' --- }}} --- -- This module implements a few useful debug functions for use throughout the -- rest of the code. -module 'irc.debug' +local irc_debug = {} -- defaults {{{ -COLOR = true +irc_debug.COLOR = true -- }}} -- local variables {{{ @@ -27,10 +23,10 @@ local outfile = io.output() -- @param msg Message text -- @param color Which terminal code to use for color output (defaults to -- dark gray) -function _message(msg_type, msg, color) +function irc_debug._message(msg_type, msg, color) if ON then local endcolor = "" - if COLOR and outfile == io.stdout then + if irc_debug.COLOR and outfile == io.stdout then color = color or "\027[1;30m" endcolor = "\027[0m" else @@ -48,9 +44,9 @@ end -- error(). -- @param msg Error message -- @see error -function _err(msg) - _message("ERR", msg, "\027[0;31m") - base.error(msg, 2) +function irc_debug._err(msg) + irc_debug._message("ERR", msg, "\027[0;31m") + error(msg, 2) end -- }}} @@ -58,8 +54,8 @@ end -- -- Signal a warning. Writes the warning message to the screen in yellow. -- @param msg Warning message -function _warn(msg) - _message("WARN", msg, "\027[0;33m") +function irc_debug._warn(msg) + irc_debug._message("WARN", msg, "\027[0;33m") end -- }}} -- }}} @@ -68,7 +64,7 @@ end -- enable {{{ --- -- Turns on debug output. -function enable() +function irc_debug.enable() ON = true end -- }}} @@ -76,7 +72,7 @@ end -- disable {{{ --- -- Turns off debug output. -function disable() +function irc_debug.disable() ON = false end -- }}} @@ -85,8 +81,10 @@ end --- -- Redirects output to a file rather than stdout. -- @param file File to write debug output to -function set_output(file) - outfile = base.assert(io.open(file)) +function irc_debug.set_output(file) + outfile = assert(io.open(file)) end -- }}} -- }}} + +return irc_debug diff --git a/src/irc/message.lua b/src/irc/message.lua index e05e87e..4471ba8 100644 --- a/src/irc/message.lua +++ b/src/irc/message.lua @@ -1,19 +1,16 @@ --- -- Implementation of IRC server message parsing -- initialization {{{ -local base = _G -local constants = require 'irc.constants' -local ctcp = require 'irc.ctcp' -local irc_debug = require 'irc.debug' -local misc = require 'irc.misc' -local socket = require 'socket' -local string = require 'string' -local table = require 'table' +local constants = libs.constants +local ctcp = libs.ctcp +local irc_debug = libs.debug +local misc = libs.misc +local socket = libs.socket -- }}} --- -- This module contains parsing functions for IRC server messages. -module 'irc.message' +local message = {} -- internal functions {{{ -- _parse {{{ @@ -32,7 +29,7 @@ module 'irc.message' -- to the received command</li> -- -- </ul> -function _parse(str) +function message._parse(str) -- low-level ctcp quoting {{{ str = ctcp._low_dequote(str) -- }}} @@ -49,8 +46,8 @@ function _parse(str) local reply = false if command:find("^%d%d%d$") then reply = true - if constants.replies[base.tonumber(command)] then - command = constants.replies[base.tonumber(command)] + if constants.replies[tonumber(command)] then + command = constants.replies[tonumber(command)] else irc_debug._warn("Unknown server reply: " .. command) end @@ -67,3 +64,5 @@ function _parse(str) end -- }}} -- }}} + +return message diff --git a/src/irc/misc.lua b/src/irc/misc.lua index 92c3ac4..d708391 100644 --- a/src/irc/misc.lua +++ b/src/irc/misc.lua @@ -1,25 +1,20 @@ --- -- Various useful functions that didn't fit anywhere else -- initialization {{{ -local base = _G -local irc_debug = require 'irc.debug' -local socket = require 'socket' -local math = require 'math' -local os = require 'os' -local string = require 'string' -local table = require 'table' +local irc_debug = libs.debug +local socket = libs.socket -- }}} --- -- This module contains various useful functions which didn't fit in any of the -- other modules. -module 'irc.misc' +local misc = {} -- defaults {{{ -DELIM = ' ' -PATH_SEP = '/' -ENDIANNESS = "big" -INT_BYTES = 4 +misc.DELIM = ' ' +misc.PATH_SEP = '/' +misc.ENDIANNESS = "big" +misc.INT_BYTES = 4 -- }}} -- private functions {{{ @@ -49,9 +44,9 @@ end -- in str will be considered one substring) -- @param rquotes String of characters to use as closing quotes -- @return Array of strings, one for each substring that was separated out -function _split(str, delim, end_delim, lquotes, rquotes) +function misc._split(str, delim, end_delim, lquotes, rquotes) -- handle arguments {{{ - delim = "["..(delim or DELIM).."]" + delim = "["..(delim or misc.DELIM).."]" if end_delim then end_delim = "["..end_delim.."]" end if lquotes then lquotes = "["..lquotes.."]" end if rquotes then rquotes = "["..rquotes.."]" end @@ -111,8 +106,8 @@ end -- @param path Path to the file -- @param sep Directory separator (optional, defaults to PATH_SEP) -- @return The basename of the file -function _basename(path, sep) - sep = sep or PATH_SEP +function misc._basename(path, sep) + sep = sep or misc.PATH_SEP if not path:find(sep) then return path end return socket.skip(2, path:find(".*" .. sep .. "(.*)")) end @@ -124,8 +119,8 @@ end -- @param path Path to the file -- @param sep Directory separator (optional, defaults to PATH_SEP) -- @return The dirname of the file -function _dirname(path, sep) - sep = sep or PATH_SEP +function misc._dirname(path, sep) + sep = sep or misc.PATH_SEP if not path:find(sep) then return "." end return socket.skip(2, path:find("(.*)" .. sep .. ".*")) end @@ -139,9 +134,9 @@ end -- @param endian Which endianness to use (big, little, host, network) (defaultsi -- to ENDIANNESS) -- @return A string whose first INT_BYTES characters make a low-level int -function _str_to_int(str, bytes, endian) - bytes = bytes or INT_BYTES - endian = endian or ENDIANNESS +function misc._str_to_int(str, bytes, endian) + bytes = bytes or misc.INT_BYTES + endian = endian or misc.ENDIANNESS local ret = "" for i = 0, bytes - 1 do local new_byte = string.char(math.fmod(str / (2^(8 * i)), 256)) @@ -159,8 +154,8 @@ end -- @param int String whose bytes correspond to the bytes of a low-level int -- @param endian Endianness of the int argument (defaults to ENDIANNESS) -- @return String representation of the low-level int argument -function _int_to_str(int, endian) - endian = endian or ENDIANNESS +function misc._int_to_str(int, endian) + endian = endian or misc.ENDIANNESS local ret = 0 for i = 1, int:len() do if endian == "big" or endian == "network" then ind = int:len() - i + 1 @@ -178,7 +173,7 @@ end -- Converts a string IP address to a low-level int. -- @param ip_str String representation of an IP address -- @return Low-level int representation of that IP address -function _ip_str_to_int(ip_str) +function misc._ip_str_to_int(ip_str) local i = 3 local ret = 0 for num in ip_str:gmatch("%d+") do @@ -195,7 +190,7 @@ end -- Converts an int to a string IP address. -- @param ip_int Low-level int representation of an IP address -- @return String representation of that IP address -function _ip_int_to_str(ip_int) +function misc._ip_int_to_str(ip_int) local ip = {} for i = 3, 0, -1 do local new_num = math.floor(ip_int / 2^(i * 8)) @@ -212,7 +207,7 @@ end -- @param filename Filename to start with -- @return Filename (same as the one we started with, except possibly with some -- numbers appended) which does not currently exist on the filesystem -function _get_unique_filename(filename) +function misc._get_unique_filename(filename) if not exists(filename) then return filename end local count = 1 @@ -231,8 +226,8 @@ end -- @param fn Function to try to call -- @param ... Arguments to fn -- @return The return values of fn, if it was successfully called -function _try_call(fn, ...) - if base.type(fn) == "function" then +function misc._try_call(fn, ...) + if type(fn) == "function" then return fn(...) end end @@ -245,8 +240,8 @@ end -- @param fn Function to try to call -- @param ... Arguments to fn -- @return The return values of fn, if it was successfully called -function _try_call_warn(msg, fn, ...) - if base.type(fn) == "function" then +function misc._try_call_warn(msg, fn, ...) + if type(fn) == "function" then return fn(...) else irc_debug._warn(msg) @@ -257,16 +252,16 @@ end -- _value_iter {{{ -- -- Iterator to iterate over just the values of a table. -function _value_iter(state, arg, pred) - for k, v in base.pairs(state) do +function misc._value_iter(state, arg, pred) + for k, v in pairs(state) do if arg == v then arg = k end end - local key, val = base.next(state, arg) + local key, val = next(state, arg) if not key then return end - if base.type(pred) == "function" then + if type(pred) == "function" then while not pred(val) do - key, val = base.next(state, key) + key, val = next(state, key) if not key then return end end end @@ -281,7 +276,7 @@ end -- @return nick -- @return username (if it exists) -- @return hostname (if it exists) -function _parse_user(user) +function misc._parse_user(user) local found, bang, nick = user:find("^([^!]*)!") if found then user = user:sub(bang + 1) @@ -301,3 +296,5 @@ function _parse_user(user) end -- }}} -- }}} + +return misc |