Compare commits

..

No commits in common. "48bd3221518d3bc806530781824d6e74a35efa83" and "f5302105f0adbebde6a0b78769393bba297372a9" have entirely different histories.

3 changed files with 54 additions and 170 deletions

View File

@ -77,12 +77,5 @@ function h.hello(cxt, msg)
end end
-- This is a task handler for a task named "hello" in the sample config, and is
-- an alias to the hello() function above.
function h.th_hello(cxt, msg)
h.hello(cxt, msg)
end
-- Hook up the handlers. -- Hook up the handlers.
itte_handlers = h itte_handlers = h

View File

@ -34,7 +34,7 @@ itte_servers = {
admins = { demouser = "password", }, admins = { demouser = "password", },
-- Scheduled tasks. -- Scheduled tasks.
tasks = { tasks = {
hello = { task1 = {
-- Interval at which tasks are performed. Options: -- Interval at which tasks are performed. Options:
-- Every 5, 10, 15, 20 or 30 minutes — e.g. "5m" for 5 minutes -- Every 5, 10, 15, 20 or 30 minutes — e.g. "5m" for 5 minutes
-- hourly, daily, weekly, monthly -- hourly, daily, weekly, monthly
@ -44,7 +44,7 @@ itte_servers = {
-- at 00:00. -- at 00:00.
time = "12:00", time = "12:00",
-- The name of the handler to run for the task. -- The name of the handler to run for the task.
handler = "th_hello", handler = "hello",
-- The channels or users where the task handler will send messages. -- The channels or users where the task handler will send messages.
recipients = { "#channel1" }, recipients = { "#channel1" },
}, },

213
itte.lua
View File

@ -25,7 +25,6 @@ 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}}",
@ -52,10 +51,9 @@ itte.config = {
dc_svr = { "conn", "Connection to {{server}} closed." }, dc_svr = { "conn", "Connection to {{server}} closed." },
exit = { "conn", "Exiting ..." }, exit = { "conn", "Exiting ..." },
listen = { "listen" }, listen = { "listen" },
names = { "names", "channel", "users" },
nickserv = { "auth_nickserv" }, nickserv = { "auth_nickserv" },
privmsg = { "privmsg", "sender", "recipient", "reply_to", "code", privmsg = { "sender", "recipient", "reply_to", "code", "code_params",
"code_params", "body" }, "body" },
redact = { "********" }, redact = { "********" },
send = { "send" }, send = { "send" },
svrs_not_found = { "config", "Error: servers not found." }, svrs_not_found = { "config", "Error: servers not found." },
@ -219,11 +217,6 @@ function itte.get_commands(svr)
check = "MODE", check = "MODE",
resp = "JOIN ", resp = "JOIN ",
}, },
names = {
-- Check and response are reversed
check = "353",
resp = "NAMES",
},
nick = { nick = {
check = "NOTICE", check = "NOTICE",
resp = "NICK " .. svr.nick, resp = "NICK " .. svr.nick,
@ -250,18 +243,6 @@ 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,
@ -570,7 +551,6 @@ itte.docs.traverse_channels = [[ (context_table, mode_str, channels_table)
]] ]]
function itte.traverse_channels(cxt, mode, chans) function itte.traverse_channels(cxt, mode, chans)
local channels = cxt.channels local channels = cxt.channels
cxt.chan_meta = {}
if chans ~= nil then channels = chans end if chans ~= nil then channels = chans end
if mode == "join" then if mode == "join" then
@ -580,7 +560,6 @@ function itte.traverse_channels(cxt, mode, chans)
for c = 1, #channels do for c = 1, #channels do
if not util.has_key(cxt.channels, channels[c]) then if not util.has_key(cxt.channels, channels[c]) then
table.insert(cxt.channels, channels[c]) table.insert(cxt.channels, channels[c])
cxt.chan_meta[channels[c]:sub(2)] = {}
end end
end end
@ -591,136 +570,68 @@ function itte.traverse_channels(cxt, mode, chans)
for c = 1, #channels do for c = 1, #channels do
if util.has_key(cxt.channels, channels[c]) then if util.has_key(cxt.channels, channels[c]) then
table.remove(cxt.channels, util.find_key(cxt.channels, channels[c])) table.remove(cxt.channels, util.find_key(cxt.channels, channels[c]))
cxt.chan_meta[channels[c]:sub(2)] = nil
end end
end end
end end
end end
itte.docs.parse_privmsg = [[ (context_table, data_str [, mode_str]) itte.docs.parse_privmsg = [[ (context_table, data_str)
Given the context table, a PRIVMSG data string and mode, parse the string and Given the context table and a PRIVMSG data string, parse the string and
return an associative table of values. If "code" mode is specified, it will return an associative table of values.
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, mode) function itte.parse_privmsg(cxt, str)
local msg = {} local code_full = ""
-- 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
local code_full = "" msg.code = code_full
-- Service code found with specified prefix msg.code_params = {}
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))
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 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("privmsg", "{ " ..
itte.config.debugs.privmsg[1] .. ": " .. msg.sender ..
", " .. itte.config.debugs.privmsg[2] .. ": " .. msg.recipient ..
", " .. itte.config.debugs.privmsg[3] .. ": " .. msg.reply_to ..
", " .. itte.config.debugs.privmsg[4] .. ": " .. msg.code ..
", " .. itte.config.debugs.privmsg[5] .. ": " .. code_params ..
", " .. itte.config.debugs.privmsg[6] .. ": " .. body ..
" }", itte.config.debug)
return msg return msg
end end
itte.docs.parse_names = [[ (context_table, data_str)
Given the context table and a NAMES data string, parse the string and
return the channel name and a table of users.
]]
function itte.parse_names(cxt, str)
-- Separator marks the start of the message body
body_sep, _ = string.find(str, ":", 2)
local channel = string.sub(str, string.find(str, "#"),
string.find(str, ":", 2) - 2)
local names = util.split_str(string.sub(str, body_sep + 1))
util.debug(itte.config.debugs.names[1],
itte.config.debugs.names[2] .. ": " .. channel ..
", " .. itte.config.debugs.names[3] .. ": { " .. table.concat(names, ", ")
.. " }", itte.config.debug)
return channel, names
end
itte.docs.get_users = [[ (context_table, channel_str)
Given the context table and a channel, return a table of users in the
channel.
]]
function itte.get_users(cxt, str)
local chan = str
if string.find(chan, "#") == 1 then chan = str:sub(2) end
if not util.has_key(cxt.chan_meta, chan) then
do return end
end
-- Query server for the latest list of names
itte.send_command(cxt.con, cxt.cmds.names.resp .. " #" .. chan)
return cxt.chan_meta[chan].users
end
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Service code handlers -- Service code handlers
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -864,10 +775,9 @@ 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 target and task handlers -- Remove task handlers
for h = 1, #custom_h do for h = 1, #custom_h do
if (string.find(custom_h[h], itte.config.target_handler) == 1) or if string.find(custom_h[h], itte.config.task_handler_prefix) == 1 then
(string.find(custom_h[h], itte.config.task_handler_prefix) == 1) then
custom_h[h] = nil custom_h[h] = nil
end end
end end
@ -930,13 +840,6 @@ function itte.listen(name, str)
itte.send_command(cxt.con, string.gsub(str, cxt.cmds.ping.check, itte.send_command(cxt.con, string.gsub(str, cxt.cmds.ping.check,
cxt.cmds.ping.resp)) cxt.cmds.ping.resp))
-- Update channel names list
elseif string.find(str, ":" .. cxt.host .. " " .. cxt.cmds.names.check) == 1
then
util.debug(itte.config.debugs.listen[1], str, itte.config.debug)
local channel, names = itte.parse_names(cxt, str)
cxt.chan_meta[channel:sub(2)] = { users = names }
-- Respond to service code -- Respond to service code
elseif util.is_substr(str, cxt.cmds.privmsg.check) then elseif util.is_substr(str, cxt.cmds.privmsg.check) then
local msg = itte.parse_privmsg(cxt, str) local msg = itte.parse_privmsg(cxt, str)
@ -949,9 +852,8 @@ 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 the target and task handlers -- Ignore calls to task handlers
if (string.find(msg.code, itte.config.target_handler) == 1) or if (string.find(msg.code, itte.config.task_handler_prefix) == 1) then
(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.
@ -968,17 +870,6 @@ 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)