From 86a0d68f9920cbcd382d8bb255639b022991def7 Mon Sep 17 00:00:00 2001 From: jluehrs2 Date: Sun, 26 Aug 2007 22:07:38 -0500 Subject: add all of the current files --- src/irc.lua | 842 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/irc/channel.lua | 331 ++++++++++++++++++++ src/irc/constants.lua | 189 +++++++++++ src/irc/ctcp.lua | 93 ++++++ src/irc/dcc.lua | 122 ++++++++ src/irc/debug.lua | 64 ++++ src/irc/message.lua | 50 +++ src/irc/misc.lua | 227 ++++++++++++++ 8 files changed, 1918 insertions(+) create mode 100644 src/irc.lua create mode 100644 src/irc/channel.lua create mode 100644 src/irc/constants.lua create mode 100644 src/irc/ctcp.lua create mode 100644 src/irc/dcc.lua create mode 100644 src/irc/debug.lua create mode 100644 src/irc/message.lua create mode 100644 src/irc/misc.lua (limited to 'src') diff --git a/src/irc.lua b/src/irc.lua new file mode 100644 index 0000000..605e74d --- /dev/null +++ b/src/irc.lua @@ -0,0 +1,842 @@ +-- initialization {{{ +local base = _G +local constants = require 'irc.constants' +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' +-- }}} + +module 'irc' + +-- constants {{{ +_VERSION = 'LuaIRC 0.2' +-- }}} + +-- classes {{{ +local Channel = base.require 'irc.channel' +-- }}} + +-- local variables {{{ +local irc_sock = nil +local rsockets = {} +local wsockets = {} +local rcallbacks = {} +local wcallbacks = {} +local icallbacks = { + whois = {}, + serverversion = {}, + servertime = {}, + ctcp_ping = {}, + ctcp_time = {}, + ctcp_version = {}, +} +local requestinfo = {whois = {}} +local handlers = {} +local ctcp_handlers = {} +local serverinfo = {} +-- }}} + +-- 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 +-- }}} + +-- private functions {{{ +-- main_loop_iter {{{ +local function main_loop_iter() + if #rsockets == 0 and #wsockets == 0 then return false end + 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 + 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') + end + end + + for _, sock in base.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') + end + end + + return true +end +-- }}} + +-- begin_main_loop {{{ +local function begin_main_loop() + while main_loop_iter() do end +end +-- }}} + +-- incoming_message {{{ +local function incoming_message(sock) + local raw_msg = socket.try(sock:receive()) + irc_debug.message("RECV", raw_msg) + 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)) + return true +end +-- }}} +-- }}} + +-- internal message handlers {{{ +-- command handlers {{{ +-- on_nick {{{ +function handlers.on_nick(from, new_nick) + for chan in channels() do + chan:change_nick(from, new_nick) + end + misc.try_call(on_nick_change, new_nick, from) +end +-- }}} + +-- on_join {{{ +function handlers.on_join(from, chan) + base.assert(serverinfo.channels[chan], + "Received join message for unknown channel: " .. chan) + if serverinfo.channels[chan].join_complete then + serverinfo.channels[chan]:add_user(from) + misc.try_call(on_join, serverinfo.channels[chan], from) + end +end +-- }}} + +-- on_part {{{ +function handlers.on_part(from, chan, part_msg) + -- don't assert on chan here, since we get part messages for ourselves + -- after we remove the channel from the channel list + if not serverinfo.channels[chan] then return end + if serverinfo.channels[chan].join_complete then + serverinfo.channels[chan]:remove_user(from) + misc.try_call(on_part, serverinfo.channels[chan], from, part_msg) + end +end +-- }}} + +-- on_mode {{{ +function handlers.on_mode(from, to, mode_string, ...) + local dir = mode_string:sub(1, 1) + mode_string = mode_string:sub(2) + local args = {...} + + if to:sub(1, 1) == "#" then + -- handle channel mode requests {{{ + base.assert(serverinfo.channels[to], + "Received mode change for unknown channel: " .. to) + local chan = serverinfo.channels[to] + local ind = 1 + for i = 1, mode_string:len() do + local mode = mode_string:sub(i, i) + local target = args[ind] + -- channel modes other than op/voice will be implemented as + -- information request commands + if mode == "o" then -- channel op {{{ + chan:change_status(target, dir == "+", "o") + misc.try_call(({["+"] = on_op, ["-"] = on_deop})[dir], + chan, from, target) + ind = ind + 1 + -- }}} + elseif mode == "v" then -- voice {{{ + chan:change_status(target, dir == "+", "v") + misc.try_call(({["+"] = on_voice, ["-"] = on_devoice})[dir], + chan, from, target) + ind = ind + 1 + -- }}} + end + end + -- }}} + elseif from == to then + -- handle user mode requests {{{ + -- TODO: make users more easily accessible so this is actually + -- reasonably possible + for i = 1, mode_string:len() do + local mode = mode_string:sub(i, i) + if mode == "i" then -- invisible {{{ + -- }}} + elseif mode == "s" then -- server messages {{{ + -- }}} + elseif mode == "w" then -- wallops messages {{{ + -- }}} + elseif mode == "o" then -- ircop {{{ + -- }}} + end + end + -- }}} + end +end +-- }}} + +-- on_topic {{{ +function handlers.on_topic(from, chan, new_topic) + base.assert(serverinfo.channels[chan], + "Received topic message for unknown channel: " .. chan) + serverinfo.channels[chan]._topic.text = new_topic + serverinfo.channels[chan]._topic.user = (misc.parse_user(from)) + serverinfo.channels[chan]._topic.time = os.time() + if serverinfo.channels[chan].join_complete then + misc.try_call(on_topic_change, serverinfo.channels[chan]) + end +end +-- }}} + +-- on_invite {{{ +function handlers.on_invite(from, to, chan) + misc.try_call(on_invite, from, chan) +end +-- }}} + +-- on_kick {{{ +function handlers.on_kick(from, chan, to) + base.assert(serverinfo.channels[chan], + "Received kick message for unknown channel: " .. chan) + if serverinfo.channels[chan].join_complete then + serverinfo.channels[chan]:remove_user(to) + misc.try_call(on_kick, serverinfo.channels[chan], to, from) + end +end +-- }}} + +-- on_privmsg {{{ +function handlers.on_privmsg(from, to, msg) + local msgs = ctcp.ctcp_split(msg, true) + for _, v in base.ipairs(msgs) do + if base.type(v) == "string" then + -- normal message {{{ + if to:sub(1, 1) == "#" then + base.assert(serverinfo.channels[to], + "Received channel msg from unknown channel: " .. to) + misc.try_call(on_channel_msg, serverinfo.channels[to], from, v) + else + misc.try_call(on_private_msg, from, v) + end + -- }}} + elseif base.type(v) == "table" then + -- ctcp message {{{ + local words = misc.split(v[1]) + local received_command = words[1] + local cb = "on_" .. received_command:lower() + 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 + ctcp_handlers[cb](from, to, table.concat(words, " ")) + else + notice(from, {"ERRMSG Unknown query: " .. received_command}) + end + -- }}} + end + end +end +-- }}} + +-- on_notice {{{ +function handlers.on_notice(from, to, msg) + local msgs = ctcp.ctcp_split(msg, true) + for _, v in base.ipairs(msgs) do + if base.type(v) == "string" then + -- normal message {{{ + if to:sub(1, 1) == "#" then + base.assert(serverinfo.channels[to], + "Received channel msg from unknown channel: " .. to) + misc.try_call(on_channel_notice, serverinfo.channels[to], + from, v) + else + misc.try_call(on_private_notice, from, v) + end + -- }}} + elseif base.type(v) == "table" then + -- ctcp message {{{ + local words = misc.split(v[1]) + local command = words[1]:lower() + table.remove(words, 1) + misc.try_call_warn("Unknown CTCP message: " .. command, + ctcp_handlers["on_rpl_"..command], from, to, + table.concat(words, ' ')) + -- }}} + end + end +end +-- }}} + +-- on_quit {{{ +function handlers.on_quit(from, quit_msg) + for name, chan in base.pairs(serverinfo.channels) do + chan:remove_user(from) + end + misc.try_call(on_quit, from, quit_msg) +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) +end +-- }}} +-- }}} + +-- server replies {{{ +-- on_rpl_topic {{{ +-- catch topic changes +function handlers.on_rpl_topic(from, chan, topic) + base.assert(serverinfo.channels[chan], + "Received topic information about unknown channel: " .. chan) + serverinfo.channels[chan]._topic.text = topic +end +-- }}} + +-- on_rpl_notopic {{{ +function handlers.on_rpl_notopic(from, chan) + base.assert(serverinfo.channels[chan], + "Received topic information about unknown channel: " .. chan) + serverinfo.channels[chan]._topic.text = "" +end +-- }}} + +-- on_rpl_topicdate {{{ +-- "topic was set by at