diff --git a/itte.lua b/itte.lua index 113f987..64b348b 100644 --- a/itte.lua +++ b/itte.lua @@ -25,6 +25,7 @@ itte.config = { debug = false, admin_handlers = { "connect", "quit", "reload", "servers", "join", "part" }, task_handler_prefix = "th_", + target_handler = "target", messages = { connect_success = "Connected to {{server}}.", help = "Service codes available: {{codes}}", @@ -249,6 +250,18 @@ function itte.get_commands(svr) check = nil, resp = "QUIT :", }, + target_private = { + -- Like privmsg but targets bot nick in a direct message + -- Escape magic character "-" + check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. ":", + resp = "PRIVMSG ", + }, + target_public = { + -- Like privmsg but targets bot nick in a channel + -- Escape magic character "-" + check = "PRIVMSG(.*)#(.*):" .. string.gsub(svr.nick, "-", "%%-"), + resp = "PRIVMSG ", + }, user = { check = "NOTICE", resp = "USER " .. svr.auth_user .. " 0 * " .. svr.auth_user, @@ -585,58 +598,91 @@ function itte.traverse_channels(cxt, mode, chans) end -itte.docs.parse_privmsg = [[ (context_table, data_str) - Given the context table and a PRIVMSG data string, parse the string and - return an associative table of values. +itte.docs.parse_privmsg = [[ (context_table, data_str [, mode_str]) + Given the context table, a PRIVMSG data string and mode, parse the string and + return an associative table of values. If "code" mode is specified, it will + check the body for a service code. If "target" mode is specified, it will + extract the message body excluding the bot nick, but will not look for a + service code. The default mode is "code" if no mode is specified. ]] -function itte.parse_privmsg(cxt, str) - local code_full = "" +function itte.parse_privmsg(cxt, str, mode) + local msg = {} -- Separator marks the start of the message body body_sep, _ = string.find(str, ":", 2) - -- Service code found with specified prefix - if util.is_substr(str, ":" .. cxt.code_prefix) then - code_full = string.sub(str, string.find(str, ":" .. - cxt.code_prefix) + 1 + string.len(cxt.code_prefix)) - end - local msg = { - sender = string.sub(str, string.find(str, "!") + 2, - string.find(str, "@") - 1), - recipient = string.sub(str, string.find(str, "PRIVMSG") + 8, - string.find(str, ":" .. cxt.code_prefix) - 2), - reply_to = string.sub(str, string.find(str, "!") + 2, - string.find(str, "@") - 1), - body = string.sub(str, body_sep + 1), - } - if util.is_substr(code_full, " ") then - msg.code = string.sub(code_full, 0, string.find(code_full, " ") - 1) - msg.code_params = util.split_str(string.sub(code_full, - string.find(code_full, " ") + 1)) + -- Message in the format `[nick]: [body]` + if mode == "target" then + msg = { + sender = string.sub(str, string.find(str, "!") + 2, + string.find(str, "@") - 1), + -- Escape magic character "-" in the nick + recipient = string.sub(str, string.find(str, "PRIVMSG") + 8, + string.find(str, ":" .. string.gsub(cxt.nick, "-", "%%-")) - 2), + reply_to = string.sub(str, string.find(str, "!") + 2, + string.find(str, "@") - 1), + body = string.gsub(string.sub(str, body_sep + 2 + + string.len(cxt.nick)), "^%s", ""), + } + -- Reply to sender by default (private message), or reply in a channel if + -- original message recipient is a channel. + if util.is_substr(msg.recipient, "#") then + msg.reply_to = msg.recipient + end + util.debug(itte.config.debugs.privmsg[1], "{ " .. + itte.config.debugs.privmsg[2] .. ": " .. msg.sender .. + ", " .. itte.config.debugs.privmsg[3] .. ": " .. msg.recipient .. + ", " .. itte.config.debugs.privmsg[4] .. ": " .. msg.reply_to .. + ", " .. itte.config.debugs.privmsg[7] .. ": " .. msg.body .. + " }", itte.config.debug) + + -- Message in the format `[code] [params]` else - msg.code = code_full - msg.code_params = {} - end - -- Reply to sender by default (private message), or reply in a channel if - -- original message recipient is a channel. - if util.is_substr(msg.recipient, "#") then - msg.reply_to = msg.recipient - end + local code_full = "" + -- Service code found with specified prefix + if util.is_substr(str, ":" .. cxt.code_prefix) then + code_full = string.sub(str, string.find(str, ":" .. + cxt.code_prefix) + 1 + string.len(cxt.code_prefix)) + end + msg = { + sender = string.sub(str, string.find(str, "!") + 2, + string.find(str, "@") - 1), + recipient = string.sub(str, string.find(str, "PRIVMSG") + 8, + string.find(str, ":" .. cxt.code_prefix) - 2), + reply_to = string.sub(str, string.find(str, "!") + 2, + string.find(str, "@") - 1), + body = string.sub(str, body_sep + 1), + } + if util.is_substr(code_full, " ") then + msg.code = string.sub(code_full, 0, string.find(code_full, " ") - 1) + msg.code_params = util.split_str(string.sub(code_full, + string.find(code_full, " ") + 1)) - -- Redact debug output for admin-only handlers to avoid logging passwords. - local body = msg.body - local code_params = table.concat(msg.code_params, " ") - if util.has_key(itte.config.admin_handlers, msg.code) then - body = itte.config.debugs.redact[1] - code_params = itte.config.debugs.redact[1] + else + msg.code = code_full + msg.code_params = {} + end + -- Reply to sender by default (private message), or reply in a channel if + -- original message recipient is a channel. + if util.is_substr(msg.recipient, "#") then + msg.reply_to = msg.recipient + end + + -- Redact debug output for admin-only handlers to avoid logging passwords. + local body = msg.body + local code_params = table.concat(msg.code_params, " ") + if util.has_key(itte.config.admin_handlers, msg.code) then + body = itte.config.debugs.redact[1] + code_params = itte.config.debugs.redact[1] + end + util.debug(itte.config.debugs.privmsg[1], "{ " .. + itte.config.debugs.privmsg[2] .. ": " .. msg.sender .. + ", " .. itte.config.debugs.privmsg[3] .. ": " .. msg.recipient .. + ", " .. itte.config.debugs.privmsg[4] .. ": " .. msg.reply_to .. + ", " .. itte.config.debugs.privmsg[5] .. ": " .. msg.code .. + ", " .. itte.config.debugs.privmsg[6] .. ": " .. code_params .. + ", " .. itte.config.debugs.privmsg[7] .. ": " .. body .. + " }", itte.config.debug) end - util.debug(itte.config.debugs.privmsg[1], "{ " .. - itte.config.debugs.privmsg[2] .. ": " .. msg.sender .. - ", " .. itte.config.debugs.privmsg[3] .. ": " .. msg.recipient .. - ", " .. itte.config.debugs.privmsg[4] .. ": " .. msg.reply_to .. - ", " .. itte.config.debugs.privmsg[5] .. ": " .. msg.code .. - ", " .. itte.config.debugs.privmsg[6] .. ": " .. code_params .. - ", " .. itte.config.debugs.privmsg[7] .. ": " .. body .. - " }", itte.config.debug) return msg end @@ -818,9 +864,10 @@ function itte._h.help(cxt, msg) end local custom_h = util.table_keys(itte.handlers) - -- Remove task handlers + -- Remove target and task handlers for h = 1, #custom_h do - if string.find(custom_h[h], itte.config.task_handler_prefix) == 1 then + if (string.find(custom_h[h], itte.config.target_handler) == 1) or + (string.find(custom_h[h], itte.config.task_handler_prefix) == 1) then custom_h[h] = nil end end @@ -902,8 +949,9 @@ function itte.listen(name, str) util.debug(itte.config.debugs.listen[1], str, itte.config.debug) end - -- Ignore calls to task handlers - if (string.find(msg.code, itte.config.task_handler_prefix) == 1) then + -- Ignore calls to the target and task handlers + if (string.find(msg.code, itte.config.target_handler) == 1) or + (string.find(msg.code, itte.config.task_handler_prefix) == 1) then do return end -- Check for the service code in the functions table before attempting to -- call the function. @@ -920,6 +968,17 @@ function itte.listen(name, str) end end + -- Respond to direct address in a channel + elseif (util.is_substr(str, cxt.cmds.target_public.check)) or + (util.is_substr(str, cxt.cmds.target_private.check)) then + util.debug(itte.config.debugs.listen[1], str, itte.config.debug) + local msg = itte.parse_privmsg(cxt, str, "target") + + -- Check for the target handler function before attempting to call it. + if util.has_key(itte.handlers, itte.config.target_handler) then + itte.handlers[itte.config.target_handler](cxt, msg) + end + -- Output to stdout anyway if debug is enabled else util.debug(itte.config.debugs.listen[1], str, itte.config.debug)