Add preliminary support for "nick: message" detection

trunk
mio 2022-04-28 06:40:57 +00:00
parent 44e56e0884
commit 562f112995
1 changed files with 108 additions and 49 deletions

157
itte.lua
View File

@ -25,6 +25,7 @@ itte.config = {
debug = false, debug = false,
admin_handlers = { "connect", "quit", "reload", "servers", "join", "part" }, admin_handlers = { "connect", "quit", "reload", "servers", "join", "part" },
task_handler_prefix = "th_", task_handler_prefix = "th_",
target_handler = "target",
messages = { messages = {
connect_success = "Connected to {{server}}.", connect_success = "Connected to {{server}}.",
help = "Service codes available: {{codes}}", help = "Service codes available: {{codes}}",
@ -249,6 +250,18 @@ function itte.get_commands(svr)
check = nil, check = nil,
resp = "QUIT :", 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 = { user = {
check = "NOTICE", check = "NOTICE",
resp = "USER " .. svr.auth_user .. " 0 * " .. svr.auth_user, resp = "USER " .. svr.auth_user .. " 0 * " .. svr.auth_user,
@ -585,58 +598,91 @@ function itte.traverse_channels(cxt, mode, chans)
end end
itte.docs.parse_privmsg = [[ (context_table, data_str) itte.docs.parse_privmsg = [[ (context_table, data_str [, mode_str])
Given the context table and a PRIVMSG data string, parse the string and Given the context table, a PRIVMSG data string and mode, parse the string and
return an associative table of values. 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) function itte.parse_privmsg(cxt, str, mode)
local code_full = "" local msg = {}
-- 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)
-- 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 else
msg.code = code_full local code_full = ""
msg.code_params = {} -- Service code found with specified prefix
end if util.is_substr(str, ":" .. cxt.code_prefix) then
-- Reply to sender by default (private message), or reply in a channel if code_full = string.sub(str, string.find(str, ":" ..
-- original message recipient is a channel. cxt.code_prefix) + 1 + string.len(cxt.code_prefix))
if util.is_substr(msg.recipient, "#") then end
msg.reply_to = msg.recipient msg = {
end 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. else
local body = msg.body msg.code = code_full
local code_params = table.concat(msg.code_params, " ") msg.code_params = {}
if util.has_key(itte.config.admin_handlers, msg.code) then end
body = itte.config.debugs.redact[1] -- Reply to sender by default (private message), or reply in a channel if
code_params = itte.config.debugs.redact[1] -- 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 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 return msg
end end
@ -818,9 +864,10 @@ function itte._h.help(cxt, msg)
end end
local custom_h = util.table_keys(itte.handlers) local custom_h = util.table_keys(itte.handlers)
-- Remove task handlers -- Remove target and task handlers
for h = 1, #custom_h do 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 custom_h[h] = nil
end end
end end
@ -902,8 +949,9 @@ function itte.listen(name, str)
util.debug(itte.config.debugs.listen[1], str, itte.config.debug) util.debug(itte.config.debugs.listen[1], str, itte.config.debug)
end end
-- Ignore calls to task handlers -- Ignore calls to the target and task handlers
if (string.find(msg.code, itte.config.task_handler_prefix) == 1) then 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 do return end
-- Check for the service code in the functions table before attempting to -- Check for the service code in the functions table before attempting to
-- call the function. -- call the function.
@ -920,6 +968,17 @@ function itte.listen(name, str)
end end
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 -- Output to stdout anyway if debug is enabled
else else
util.debug(itte.config.debugs.listen[1], str, itte.config.debug) util.debug(itte.config.debugs.listen[1], str, itte.config.debug)