diff --git a/itte.lua b/itte.lua index 64b348b..6bf5702 100644 --- a/itte.lua +++ b/itte.lua @@ -251,16 +251,31 @@ function itte.get_commands(svr) resp = "QUIT :", }, target_private = { + -- Like privmsg but targets bot nick in a direct message without a + -- service code. + -- Escape magic character "-" + check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. "(.*):", + resp = nil, + }, + target_private_code = { -- Like privmsg but targets bot nick in a direct message -- Escape magic character "-" - check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. ":", - resp = "PRIVMSG ", + check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. "(.*):" .. + string.gsub(svr.nick, "-", "%%-") .. "(.*)" .. svr.code_prefix, + resp = nil, }, target_public = { - -- Like privmsg but targets bot nick in a channel + -- Like privmsg but targets bot nick in a channel without a service code. -- Escape magic character "-" check = "PRIVMSG(.*)#(.*):" .. string.gsub(svr.nick, "-", "%%-"), - resp = "PRIVMSG ", + resp = nil, + }, + target_public_code = { + -- Like privmsg but targets bot nick in a channel + -- Escape magic character "-" + check = "PRIVMSG(.*)#(.*):" .. string.gsub(svr.nick, "-", "%%-") .. + "(.*)" .. svr.code_prefix, + resp = nil, }, user = { check = "NOTICE", @@ -610,18 +625,16 @@ function itte.parse_privmsg(cxt, str, mode) -- Separator marks the start of the message body body_sep, _ = string.find(str, ":", 2) - -- Message in the format `[nick]: [body]` + -- Message in the format `[body]` (private message) or `[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), + string.find(str, " :") - 1), 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", ""), + body = string.sub(str, body_sep + 1), } -- Reply to sender by default (private message), or reply in a channel if -- original message recipient is a channel. @@ -635,19 +648,31 @@ function itte.parse_privmsg(cxt, str, mode) ", " .. itte.config.debugs.privmsg[7] .. ": " .. msg.body .. " }", itte.config.debug) - -- Message in the format `[code] [params]` + -- Message in the format `[code] [params]` or `[nick] [code] [params]` else + -- Set the code prefix position variable to a number out of range of the + -- service code parsing buffer, then get the position. Check for the colon + -- separator at exactly the start of the message body, i.e. `[code]`, or + -- the space char for `[nick] [code]`. 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)) + local cp_pos = body_sep + string.len(cxt.nick) + 4 + local co_sep, _ = string.find(str, ":" .. cxt.code_prefix) + if co_sep == body_sep then + cp_pos = string.find(str, ":" .. cxt.code_prefix) + elseif string.find(str, " " .. cxt.code_prefix) ~= nil then + cp_pos = string.find(str, " " .. cxt.code_prefix) + end + -- Only parse service code if it is near the start of the message body, + -- within a small buffer in position to allow some variation in style, e.g. + -- `[nick]: [code]`, `[nick] [code]`, etc. + if cp_pos <= body_sep + string.len(cxt.nick) + 3 then + code_full = string.sub(str, cp_pos + 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), + string.find(str, " :") - 1), reply_to = string.sub(str, string.find(str, "!") + 2, string.find(str, "@") - 1), body = string.sub(str, body_sep + 1), @@ -656,7 +681,6 @@ function itte.parse_privmsg(cxt, str, mode) 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)) - else msg.code = code_full msg.code_params = {} @@ -666,7 +690,6 @@ function itte.parse_privmsg(cxt, str, mode) 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, " ") @@ -937,8 +960,12 @@ function itte.listen(name, str) local channel, names = itte.parse_names(cxt, str) cxt.chan_meta[channel:sub(2)] = { users = names } - -- Respond to service code - elseif util.is_substr(str, cxt.cmds.privmsg.check) then + -- Respond to service code, checking for `[code]` and `[nick]: [code]`. + -- Code prefix cannot be the last character in the data string. + elseif (util.is_substr(str, cxt.cmds.privmsg.check) or + (util.is_substr(str, cxt.cmds.target_public_code.check)) or + (util.is_substr(str, cxt.cmds.target_private_code.check))) and + (str:sub(string.len(str)) ~= cxt.code_prefix) then local msg = itte.parse_privmsg(cxt, str) -- Check for the service in the admin handlers table and redact the debug -- output for admin handlers. @@ -960,6 +987,12 @@ function itte.listen(name, str) -- Check the handlers table elseif util.has_key(itte.handlers, msg.code) then itte.handlers[msg.code](cxt, msg) + -- If there is no service code found near the start of the message body, + -- respond as a direct address and pass the message to the target handler + -- if it exists. + elseif (util.has_key(itte.handlers, itte.config.target_handler)) and + (msg.code == "") then + itte.handlers[itte.config.target_handler](cxt, msg) else -- Only hint with unknown code error in private messages as there may be -- prefix collision in channels.