Change the behaviour of service code detection

- Parse service codes if they are sent as `!code` or `nick: !code` in channels
  and private messages (where `!code` is an example code prefix and service
  code, and `nick` is the bot's nick). If the service code is not provided at
  the start of the message body, it will be treated as a message without a
  service code and passed to the target handler.

- If a target handler is defined, both messages in the form `nick: message`
  in a channel and `message` in a private message will be sent to the handler.
  The `nick:` at the start of `nick: message` will no longer be filtered out.

  The default name of the target handler is `target`, as in
  `itte_handlers.target(cxt, msg)`. This can be changed in the config by
  setting a new `itte_config.target_handler` value.
trunk
mio 2022-04-29 03:16:30 +00:00
parent 562f112995
commit 8e84095be6
1 changed files with 52 additions and 19 deletions

View File

@ -251,16 +251,31 @@ function itte.get_commands(svr)
resp = "QUIT :", resp = "QUIT :",
}, },
target_private = { 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 -- Like privmsg but targets bot nick in a direct message
-- Escape magic character "-" -- Escape magic character "-"
check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. ":", check = "PRIVMSG(.*)" .. string.gsub(svr.nick, "-", "%%-") .. "(.*):" ..
resp = "PRIVMSG ", string.gsub(svr.nick, "-", "%%-") .. "(.*)" .. svr.code_prefix,
resp = nil,
}, },
target_public = { 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 "-" -- Escape magic character "-"
check = "PRIVMSG(.*)#(.*):" .. string.gsub(svr.nick, "-", "%%-"), 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 = { user = {
check = "NOTICE", check = "NOTICE",
@ -610,18 +625,16 @@ function itte.parse_privmsg(cxt, str, mode)
-- Separator marks the start of the message body -- Separator marks the start of the message body
body_sep, _ = string.find(str, ":", 2) 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 if mode == "target" then
msg = { msg = {
sender = string.sub(str, string.find(str, "!") + 2, sender = string.sub(str, string.find(str, "!") + 2,
string.find(str, "@") - 1), string.find(str, "@") - 1),
-- Escape magic character "-" in the nick
recipient = string.sub(str, string.find(str, "PRIVMSG") + 8, 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, reply_to = string.sub(str, string.find(str, "!") + 2,
string.find(str, "@") - 1), string.find(str, "@") - 1),
body = string.gsub(string.sub(str, body_sep + 2 + body = string.sub(str, body_sep + 1),
string.len(cxt.nick)), "^%s", ""),
} }
-- Reply to sender by default (private message), or reply in a channel if -- Reply to sender by default (private message), or reply in a channel if
-- original message recipient is a channel. -- 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.debugs.privmsg[7] .. ": " .. msg.body ..
" }", itte.config.debug) " }", itte.config.debug)
-- Message in the format `[code] [params]` -- Message in the format `[code] [params]` or `[nick] [code] [params]`
else 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 = "" local code_full = ""
-- Service code found with specified prefix local cp_pos = body_sep + string.len(cxt.nick) + 4
if util.is_substr(str, ":" .. cxt.code_prefix) then local co_sep, _ = string.find(str, ":" .. cxt.code_prefix)
code_full = string.sub(str, string.find(str, ":" .. if co_sep == body_sep then
cxt.code_prefix) + 1 + string.len(cxt.code_prefix)) 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 end
msg = { msg = {
sender = string.sub(str, string.find(str, "!") + 2, sender = string.sub(str, string.find(str, "!") + 2,
string.find(str, "@") - 1), string.find(str, "@") - 1),
recipient = string.sub(str, string.find(str, "PRIVMSG") + 8, 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, reply_to = string.sub(str, string.find(str, "!") + 2,
string.find(str, "@") - 1), string.find(str, "@") - 1),
body = string.sub(str, body_sep + 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 = string.sub(code_full, 0, string.find(code_full, " ") - 1)
msg.code_params = util.split_str(string.sub(code_full, msg.code_params = util.split_str(string.sub(code_full,
string.find(code_full, " ") + 1)) string.find(code_full, " ") + 1))
else else
msg.code = code_full msg.code = code_full
msg.code_params = {} msg.code_params = {}
@ -666,7 +690,6 @@ function itte.parse_privmsg(cxt, str, mode)
if util.is_substr(msg.recipient, "#") then if util.is_substr(msg.recipient, "#") then
msg.reply_to = msg.recipient msg.reply_to = msg.recipient
end end
-- Redact debug output for admin-only handlers to avoid logging passwords. -- Redact debug output for admin-only handlers to avoid logging passwords.
local body = msg.body local body = msg.body
local code_params = table.concat(msg.code_params, " ") 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) local channel, names = itte.parse_names(cxt, str)
cxt.chan_meta[channel:sub(2)] = { users = names } cxt.chan_meta[channel:sub(2)] = { users = names }
-- Respond to service code -- Respond to service code, checking for `[code]` and `[nick]: [code]`.
elseif util.is_substr(str, cxt.cmds.privmsg.check) then -- 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) local msg = itte.parse_privmsg(cxt, str)
-- Check for the service in the admin handlers table and redact the debug -- Check for the service in the admin handlers table and redact the debug
-- output for admin handlers. -- output for admin handlers.
@ -960,6 +987,12 @@ function itte.listen(name, str)
-- Check the handlers table -- Check the handlers table
elseif util.has_key(itte.handlers, msg.code) then elseif util.has_key(itte.handlers, msg.code) then
itte.handlers[msg.code](cxt, msg) 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 else
-- Only hint with unknown code error in private messages as there may be -- Only hint with unknown code error in private messages as there may be
-- prefix collision in channels. -- prefix collision in channels.