@ -23,22 +23,40 @@ itte.confs = {
-- Default application settings
itte.config = {
debug = false ,
notify_errors = false ,
admin_handlers = { " connect " , " quit " , " reload " , " servers " , " join " , " part " } ,
messages = {
connect_success = " Connected to {{server}}. " ,
help = " Service codes available: {{codes}} " ,
join = " We're in. " ,
list_all_servers = " Known servers: {{servers}} " ,
list_channels = " Channels: {{channels}} " ,
list_conn_servers = " Connected servers: {{servers}} " ,
part = " The bot is greater than the sum of its parts. " ,
ping = " Pong! " ,
quit = " Goodbye. " ,
quit_success = " Quit {{server}}. " ,
reload = " Splines recticulated. " ,
} ,
errors = {
invalid_name = " Error: invalid server name `{{server}}`. " ,
no_perm = " I'm sorry, {{user}}. I'm afraid I can't do that. " ,
notify_no_perm = " {{user}} attempted to use service code: {{code}} " ,
ns_auth_failed = " Error: Nickserv identification failed. " ,
sasl_auth_failed = " Error: SASL authentication failed. " ,
unknown_code = " I don't understand what you just said. " ,
} ,
debugs = {
cap = { " auth_cap " } ,
conn_svr = { " conn " , " Connecting to {{server}} ... " } ,
dc_svr = { " conn " , " Connection to {{server}} closed. " } ,
exit = { " conn " , " Exiting ... " } ,
listen = { " listen " } ,
nickserv = { " auth_nickserv " } ,
privmsg = { " sender " , " recipient " , " reply_to " , " code " , " code_params " ,
" body " } ,
redact = { " ******** " } ,
send = { " send " } ,
svrs_not_found = { " config " , " Error: servers not found. " } ,
} ,
}
-- Store server info
@ -79,7 +97,7 @@ function itte.help(name)
end
itte.docs . get_config = [ [ ( )
itte.docs . get_config = [ [ ( [reload_bool ] )
Load the config file .
] ]
function itte . get_config ( reload )
@ -101,23 +119,29 @@ function itte.get_config(reload)
if itte.servers ~= nil then
itte.servers = itte_servers
else
util.debug ( " config " , " Error: servers not found. " , itte.config . debug )
util.debug ( itte.config . debugs.svrs_not_found [ 1 ] ,
itte.config . debugs.svrs_not_found [ 2 ] , itte.config . debug )
do return end
end
if itte_config ~= nil then itte.config = itte_config end
-- Update config with value overrides from itte_config
if itte_config ~= nil then
for k , v in pairs ( itte_config ) do
itte.config [ k ] = v
end
end
if itte_handlers ~= nil then itte.handlers = itte_handlers end
if itte_admins ~= nil then itte.admins = itte_admins end
-- If reloading, reconstruct the context tables.
-- This only works if the instance name has not changed between reloads.
-- This only works if the nam e name has not changed between reloads.
if reload then
for instanc e, prop in pairs ( itte.servers ) do
cxt_old = itte.contexts [ instanc e]
itte.contexts [ instanc e] = prop
itte.contexts [ instance] . name = instanc e
itte.contexts [ instanc e] . cmds = itte.get_commands ( prop )
itte.contexts [ instanc e] . con = cxt_old.con
itte.contexts [ instanc e] . con : settimeout ( 0.5 )
for nam e, prop in pairs ( itte.servers ) do
cxt_old = itte.contexts [ nam e]
itte.contexts [ nam e] = prop
itte.contexts [ name] . name = nam e
itte.contexts [ nam e] . cmds = itte.get_commands ( prop )
itte.contexts [ nam e] . con = cxt_old.con
itte.contexts [ nam e] . con : settimeout ( 0.5 )
end
end
end
@ -211,12 +235,18 @@ function itte.get_commands(svr)
end
itte.docs . send_command = [ [ ( context_table, cmd_str )
itte.docs . send_command = [ [ ( socket_obj, command_str [ , redact_bool ] )
Format a IRC command string to send through a socket connection .
If ` redact ` is set to true , the debug output contents will be redacted .
] ]
function itte . send_command ( con , str )
function itte . send_command ( con , str , redact )
con : send ( str .. " \r \n " )
util.debug ( " send " , str , itte.config . debug )
if redact == true then
util.debug ( itte.config . debugs.send [ 1 ] , itte.config . debugs.redact [ 1 ] ,
itte.config . debug )
else
util.debug ( itte.config . debugs.send [ 1 ] , str , itte.config . debug )
end
end
@ -231,10 +261,10 @@ function itte.message(cxt, users, str)
end
itte.docs . connect_server = [ [ ( name_str , server_table )
itte.docs . init_socket = [ [ ( name_str , server_table )
Initialise a socket connection to a server and return a context table .
] ]
function itte . connect_server ( name , svr )
function itte . init_socket ( name , svr )
-- Load server context
local context = svr
context.name = name
@ -259,8 +289,9 @@ function itte.connect_server(name, svr)
options = " all " ,
}
util.debug ( " conn " , " Connecting to " .. svr.host .. " / " .. svr.port ..
" ... " , itte.config . debug )
util.debug ( itte.config . debugs.conn_svr [ 1 ] ,
string.gsub ( itte.config . debugs.conn_svr [ 2 ] ,
" {{server}} " , svr.host .. " / " .. svr.port ) , itte.config . debug )
context.con : connect ( svr.host , svr.port )
context.con = ssl.wrap ( context.con , con_params )
context.con : dohandshake ( )
@ -275,12 +306,63 @@ function itte.connect_server(name, svr)
end
itte.docs . connect_server = [ [ ( name_str )
Connect to a server and join the channel ( s ) specified in the config .
] ]
function itte . connect_server ( name )
itte.contexts [ name ] = itte.init_socket ( name , itte.servers [ name ] )
-- For PASS-based authentication, send PASS before greeting the server
if itte.contexts [ name ] . auth_type == " pass " then
itte.send_command ( itte.contexts [ name ] . con ,
itte.contexts [ name ] . cmds.pass . resp , true )
itte.contexts [ name ] . state.authed = true
end
-- Greet the server in all auth scenarios except SASL, which greets the
-- server during CAP negotiation.
if ( itte.contexts [ name ] . auth_type ~= " sasl " ) then
itte.send_command ( itte.contexts [ name ] . con ,
itte.contexts [ name ] . cmds.user . resp )
itte.send_command ( itte.contexts [ name ] . con ,
itte.contexts [ name ] . cmds.nick . resp )
end
local joined_chans = false
while ( itte.contexts [ name ] . state.connected ) and ( not joined_chans ) do
local data , status = itte.contexts [ name ] . con : receive ( )
if itte.contexts [ name ] . auth_type == " sasl " then
itte.negotiate_cap ( itte.contexts [ name ] , data )
elseif itte.contexts [ name ] . auth_type == " nickserv " then
itte.auth_nickserv ( itte.contexts [ name ] , data )
end
-- Minimum requirements for joining channels: client has authenticated if
-- an auth type is set, or has received a NOTICE [nick] message during an
-- ident lookup.
if itte.contexts [ name ] . state.authed then
itte.traverse_channels ( itte.contexts [ name ] , " join " )
joined_chans = true
elseif ( data ~= nil ) then
if util.is_substr ( data , itte.contexts [ name ] . cmds.join . check )
then
itte.traverse_channels ( itte.contexts [ name ] , " join " )
joined_chans = true
end
end
end
end
itte.docs . disconnect_server = [ [ ( name_str )
Close a socket connection to a server .
] ]
function itte . disconnect_server ( name )
if itte.contexts [ name ] ~= nil then
util.debug ( " conn " , " Connection to " .. name .. " closed. " ,
util.debug ( itte.config . debugs.dc_svr [ 1 ] ,
string.gsub ( itte.config . debugs.dc_svr [ 2 ] , " {{server}} " , name ) ,
itte.config . debug )
end
itte.contexts [ name ] . con : close ( )
@ -289,7 +371,8 @@ function itte.disconnect_server(name)
-- Check if it is the last connection and trigger client exit
-- if no connections remain
if # util.table_keys ( itte.contexts ) == 0 then
util.debug ( " conn " , " Exiting ... " , itte.config . debug )
util.debug ( itte.config . debugs.exit [ 1 ] , itte.config . debugs.exit [ 2 ] ,
itte.config . debug )
itte.state . connected = false
end
end
@ -307,7 +390,7 @@ function itte.negotiate_cap(cxt, str)
end
if ( str ~= nil ) then
util.debug ( " auth " , str , itte.config . debug )
util.debug ( itte.config . debugs.cap [ 1 ] , str , itte.config . debug )
-- Wait for server to respond with capabilities list before greeting
if ( not cxt.state . cap_greeted ) then
@ -329,7 +412,7 @@ function itte.negotiate_cap(cxt, str)
-- Format of the string to encode: "user\0user\0password"
local sasl_pass , _ = mime.b64 ( cxt.auth_user .. " \0 " .. cxt.auth_user ..
" \0 " .. cxt.auth_pass )
itte.send_command ( cxt.con , cxt.cmds . auth_pass.resp .. sasl_pass )
itte.send_command ( cxt.con , cxt.cmds . auth_pass.resp .. sasl_pass , true )
-- Look for auth success signal and end cap negotiation
elseif ( util.is_substr ( str , cxt.cmds . cap_end.check ) ) then
@ -353,10 +436,10 @@ itte.docs.auth_nickserv = [[ (context_table, data_str)
] ]
function itte . auth_nickserv ( cxt , str )
if str ~= nil then
util.debug ( " auth " , str , itte.config . debug )
util.debug ( itte.config . debugs.nickserv [ 1 ] , str , itte.config . debug )
if util.is_substr ( str , cxt.cmds . ns_identify.check ) then
itte.send_command ( cxt.con , cxt.cmds . ns_identify.resp )
itte.send_command ( cxt.con , cxt.cmds . ns_identify.resp , true )
cxt.state . ns_checked = true
elseif util.is_substr ( str , cxt.cmds . ns_identify_pass.check ) then
cxt.state . authed = true
@ -370,9 +453,22 @@ function itte.auth_nickserv(cxt, str)
end
itte.docs . is_admin = [ [ ( admins_table , message_table , mode_str )
Check whether a user is an admin . Modes : global , instance . If ` mode ` is
unspecified , check for the user in the instance ' s admins table. Return true
itte.docs . is_pm = [ [ ( message_table )
Check whether a message is a private message . Return true if it is a private
message or false otherwise .
] ]
function itte . is_pm ( msg )
if util.is_substr ( msg.reply_to , " # " ) then
return false
else
return true
end
end
itte.docs . is_admin = [ [ ( admins_table , message_table [ , mode_str ] )
Check whether a user is an admin . Modes : global , name . If ` mode ` is
unspecified , check for the user in the name ' s admins table. Return true
if the user and password are in the given admin table , or false otherwise .
] ]
function itte . is_admin ( admins , msg , mode )
@ -380,12 +476,15 @@ function itte.is_admin(admins, msg, mode)
-- If no admin users set or no password provided
if ( admins == nil ) or ( table.concat ( msg.code_params ) == " " ) then
do return false end
-- Incorrect password provided
elseif msg.code_params ~= nil then
if mode == " global " then
-- For global admins, both user and password are supplied in the message
-- to keep the auth network-independent
in_admins = util.is_entry ( admins , msg.code_params [ 1 ] ,
-- No password provided in global mode
if ( mode == " global " ) and ( # util.table_keys ( msg.code_params ) < 2 ) then
do return false end
elseif ( mode == " global " ) then
-- For global admins, both user and password are supplied as the last two
-- values in the message parameters to keep the auth network-independent.
in_admins = util.is_entry ( admins , msg.code_params [ # msg.code_params - 1 ] ,
msg.code_params [ # msg.code_params ] )
else
-- User is the IRC user, password is suppled in the message
@ -393,6 +492,7 @@ function itte.is_admin(admins, msg, mode)
msg.code_params [ # msg.code_params ] )
end
-- Incorrect password provided
if not in_admins then
do return false end
else
@ -402,18 +502,25 @@ function itte.is_admin(admins, msg, mode)
end
itte.docs . notify_no_perms = [ [ ( context_table , message_table )
Respond to an attempt to use service code by a non - admin user and send a
message to the admins if ` notify_errors ` is enabled in the config .
itte.docs . is_allowed = [ [ ( context_table , message_table [ , mode_str ] )
Check if user is allowed to run a handler . If the handler is for admins only ,
check whether the request was sent in a private message and the user is an
admin ( authorised ) . Set ` mode ` to " global " to check for global admins .
Returns true if the user is allowed or false otherwise .
] ]
function itte . notify_no_perms ( cxt , msg )
itte.message ( cxt , { msg.reply_to } , string.gsub ( itte.config . errors.no_perm ,
" {{user}} " , msg.sender ) )
if ( itte.config . notify_errors ) and ( cxt.admins ~= nil ) then
local notify_msg = string.gsub ( itte.config . errors.notify_no_perm ,
" {{user}} " , msg.sender )
notify_msg = string.gsub ( notify_msg , " {{code}} " , msg.code )
itte.message ( cxt , util.table_keys ( cxt.admins ) , notify_msg )
function itte . is_allowed ( cxt , msg , mode )
if not util.has_key ( itte.config . admin_handlers , msg.code ) then
return true
else
local admins = cxt.admins
if mode == " global " then admins = itte.admins end
if ( itte.is_pm ( msg ) ) and itte.is_admin ( admins , msg , mode ) then
return true
else
itte.message ( cxt , { msg.reply_to } ,
string.gsub ( itte.config . errors.no_perm , " {{user}} " , msg.sender ) )
return false
end
end
end
@ -430,10 +537,22 @@ function itte.traverse_channels(cxt, mode, chans)
if mode == " join " then
itte.send_command ( cxt.con , cxt.cmds . join.resp .. table.concat ( channels ,
" , " ) )
-- Append to the channels table
for c = 1 , # channels do
if not util.has_key ( cxt.channels , channels [ c ] ) then
table.insert ( cxt.channels , channels [ c ] )
end
end
elseif mode == " part " then
itte.send_command ( cxt.con , cxt.cmds . part.resp .. table.concat ( channels ,
" , " ) )
-- Remove from the channels table
for c = 1 , # channels do
if util.has_key ( cxt.channels , channels [ c ] ) then
table.remove ( cxt.channels , util.find_key ( cxt.channels , channels [ c ] ) )
end
end
end
end
@ -475,13 +594,20 @@ function itte.parse_privmsg(cxt, str)
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 " , " { " ..
" sender: " .. msg.sender ..
" , recipient: " .. msg.recipient ..
" , reply_to: " .. msg.reply_to ..
" , code: " .. msg.code ..
" , code_params: " .. table.concat ( msg.code_params , " " ) ..
" , body: " .. msg.body ..
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
end
@ -491,35 +617,113 @@ end
-- Service code handlers
-- ---------------------------------------------------------------------------
itte.docs . _h.help = [ [ ( context_table , message_table )
Send a help message listing available service codes .
] ]
-- Send a help message listing available service codes.
function itte . _h . help ( cxt , msg )
local custom_h = util.table_keys ( itte.handlers )
local codes = cxt.code_prefix .. table.concat ( custom_h , " , " ..
cxt.code_prefix )
-- Core service codes are shown only to admins
if itte.is_admin ( cxt.admins , msg ) then
local core_h = util.table_keys ( itte._h )
codes = cxt.code_prefix .. table.concat ( core_h , " , " .. cxt.code_prefix ) ..
" , " .. cxt.code_prefix .. table.concat ( custom_h , " , " ..
cxt.code_prefix )
itte.docs . _h.connect = [ [ ( context_table , message_table )
Connect to a server .
] ]
function itte . _h . connect ( cxt , msg )
if not itte.is_allowed ( cxt , msg , " global " ) then
do return end
end
if # util.table_keys ( msg.code_params ) > 2 then
-- Look up name and connect to the server
for s = 1 , # msg.code_params - 2 do
if util.has_key ( itte.servers , msg.code_params [ s ] ) then
itte.connect_server ( msg.code_params [ s ] )
local succ_str = string.gsub ( itte.config . messages.connect_success ,
" {{server}} " , msg.code_params [ s ] )
itte.message ( cxt , { msg.reply_to } , succ_str )
else
local err_str = string.gsub ( itte.config . errors.invalid_name ,
" {{server}} " , msg.code_params [ s ] )
itte.message ( cxt , { msg.reply_to } , err_str )
end
end
else
local err_str = string.gsub ( itte.config . errors.invalid_name ,
" {{server}} " , msg.code_params [ 1 ] )
itte.message ( cxt , { msg.reply_to } , err_str )
end
end
itte.docs . _h.quit = [ [ ( context_table , message_table )
Disconnect from a server or multiple servers .
] ]
function itte . _h . quit ( cxt , msg )
if not itte.is_allowed ( cxt , msg , " global " ) then
do return end
end
-- If no server is specified, quit the server name where the message
-- originated
if # util.table_keys ( msg.code_params ) == 2 then
itte.send_command ( cxt.con , cxt.cmds . quit.resp .. itte.config . messages.quit )
else
-- Look up name and send quit message to the specified server(s)
for s = 1 , # msg.code_params - 2 do
if util.has_key ( itte.contexts , msg.code_params [ s ] ) then
itte.send_command ( itte.contexts [ msg.code_params [ s ] ] . con ,
itte.contexts [ msg.code_params [ s ] ] . cmds.quit . resp ..
itte.config . messages.quit )
local succ_str = string.gsub ( itte.config . messages.quit_success ,
" {{server}} " , msg.code_params [ s ] )
itte.message ( cxt , { msg.reply_to } , succ_str )
else
local err_str = string.gsub ( itte.config . errors.invalid_name ,
" {{server}} " , msg.code_params [ s ] )
itte.message ( cxt , { msg.reply_to } , err_str )
end
end
end
end
itte.docs . _h.reload = [ [ ( context_table , message_table )
Reload the server config .
] ]
function itte . _h . reload ( cxt , msg )
if not itte.is_allowed ( cxt , msg , " global " ) then
do return end
end
itte.get_config ( true )
itte.message ( cxt , { msg.reply_to } , itte.config . messages.reload )
end
itte.docs . _h.servers = [ [ ( context_table , message_table )
List known servers .
] ]
function itte . _h . servers ( cxt , msg )
if not itte.is_allowed ( cxt , msg , " global " ) then
do return end
end
if itte.contexts ~= nil then
local conn_svrs_str = string.gsub ( itte.config . messages.list_conn_servers ,
" {{servers}} " , table.concat ( util.table_keys ( itte.contexts ) , " , " ) )
itte.message ( cxt , { msg.reply_to } , conn_svrs_str )
end
if itte.servers ~= nil then
local all_svrs_str = string.gsub ( itte.config . messages.list_all_servers ,
" {{servers}} " , table.concat ( util.table_keys ( itte.servers ) , " , " ) )
itte.message ( cxt , { msg.reply_to } , all_svrs_str )
end
local help_msg = string.gsub ( itte.config . messages.help , " {{codes}} " , codes )
itte.message ( cxt , { msg.reply_to } , help_msg )
end
itte.docs . _h.join = [ [ ( context_table , message_table )
Join specified channels .
Join specified channels on the current server .
] ]
function itte . _h . join ( cxt , msg )
if not itte.is_admin ( cxt.admins , msg ) then
itte.notify_no_perms ( cxt , msg )
if not itte.is_allowed ( cxt , msg ) then
do return end
end
if msg.code_params ~= { } then
-- Trim the last parameter, which is a password and not a channel
table.remove ( msg.code_params )
itte.traverse_channels ( cxt , " join " , msg.code_params )
itte.message ( cxt , { msg.reply_to } , itte.config . messages.join )
end
@ -527,50 +731,70 @@ end
itte.docs . _h.part = [ [ ( context_table , message_table )
Leave specified channels .
Leave specified channels on the current server .
] ]
function itte . _h . part ( cxt , msg )
if not itte.is_admin ( cxt.admins , msg ) then
itte.notify_no_perms ( cxt , msg )
if not itte.is_allowed ( cxt , msg ) then
do return end
end
if msg.code_params ~= { } then
-- Trim the last parameter, which is a password and not a channel
table.remove ( msg.code_params )
itte.traverse_channels ( cxt , " part " , msg.code_params )
itte.message ( cxt , { msg.reply_to } , itte.config . messages.part )
end
end
itte.docs . _h.ping = [ [ ( context_table , message_table )
Send a " pong " message .
] ]
function itte . _h . ping ( cxt , msg )
itte.message ( cxt , { msg.reply_to } , itte.config . messages.ping )
itte.docs . _h.help = [ [ ( context_table , message_table )
Send a help message listing available service codes .
] ]
function itte . _h . help ( cxt , msg )
if not itte.is_allowed ( cxt , msg ) then
do return end
end
local custom_h = util.table_keys ( itte.handlers )
local codes = cxt.code_prefix .. table.concat ( custom_h , " , " ..
cxt.code_prefix )
-- Core service codes are shown only to admins
if itte.is_admin ( cxt.admins , msg ) then
local core_h = util.table_keys ( itte._h )
codes = cxt.code_prefix .. table.concat ( core_h , " , " .. cxt.code_prefix ) ..
" , " .. cxt.code_prefix .. table.concat ( custom_h , " , " ..
cxt.code_prefix )
end
local help_str = string.gsub ( itte.config . messages.help , " {{codes}} " , codes )
itte.message ( cxt , { msg.reply_to } , help_str )
end
itte.docs . _h.quit = [ [ ( context_table , message_table )
Disconnect from the server .
itte.docs . _h. channels = [ [ ( context_table , message_table )
List the known channels for the current server .
] ]
function itte . _h . quit ( cxt , msg )
if not itte.is_admin ( itte.admins , msg , " global " ) then
itte.notify_no_perms ( cxt , msg )
function itte . _h . channels ( cxt , msg )
if not itte.is_allowed ( cxt , msg ) then
do return end
end
itte.send_command ( cxt.con , cxt.cmds . quit.resp .. itte.config . messages.quit )
if cxt.channels ~= nil then
local channels_str = string.gsub ( itte.config . messages.list_channels ,
" {{channels}} " , table.concat ( cxt.channels , " , " ) )
itte.message ( cxt , { msg.reply_to } , channels_str )
end
end
itte.docs . _h.reload = [ [ ( context_table , message_table )
Reload the server config .
itte.docs . _h. ping = [ [ ( context_table , message_table )
Send a " pong " message .
] ]
function itte . _h . reload ( cxt , msg )
if not itte.is_admin ( itte.admins , msg , " global " ) then
itte.notify_no_perms ( cxt , msg )
function itte . _h . ping ( cxt , msg )
if not itte.is_allowed ( cxt , msg ) then
do return end
end
itte.get_config ( true )
itte.message ( cxt , { msg.reply_to } , itte.config . messages. reload )
itte.message ( cxt , { msg.reply_to } , itte.config . messages. ping )
end
@ -583,18 +807,26 @@ itte.docs.listen = [[ (name_str, data_str)
string matches preset patterns .
] ]
function itte . listen ( name , str )
util.debug ( " listen " , str , itte.config . debug )
local cxt = itte.contexts [ name ]
-- Respond to server ping
if util.is_substr ( str , cxt.cmds . ping.check ) then
util.debug ( itte.config . debugs.listen [ 1 ] , str , itte.config . debug )
itte.send_command ( cxt.con , string.gsub ( str , cxt.cmds . ping.check ,
cxt.cmds . ping.resp ) )
-- Respond to service code
elseif util.is_substr ( str , cxt.cmds . privmsg.check ) 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.
if util.has_key ( itte.config . admin_handlers , msg.code ) then
util.debug ( itte.config . debugs.listen [ 1 ] , itte.config . debugs.redact [ 1 ] ,
itte.config . debug )
else
util.debug ( itte.config . debugs.listen [ 1 ] , str , itte.config . debug )
end
-- Check for the service code in the functions table before attempting to
-- call the function.
if util.has_key ( itte._h , msg.code ) then
@ -603,8 +835,8 @@ function itte.listen(name, str)
elseif util.has_key ( itte.handlers , msg.code ) then
itte.handlers [ msg.code ] ( cxt , msg )
else
-- Only hint with unknown code error in private messages
-- as there may be prefix collision in channels.
-- Only hint with unknown code error in private messages as there may be
-- prefix collision in channels.
if not util.is_substr ( msg.reply_to , " # " ) then
itte.message ( cxt , { msg.reply_to } , itte.config . errors.unknown_code )
end
@ -635,57 +867,12 @@ itte.docs.run = [[ ()
function itte . run ( )
itte.get_config ( )
-- Connect to servers and get context with socket ref for each server
itte.contexts = { }
for instance , prop in pairs ( itte.servers ) do
itte.contexts [ instance ] = itte.connect_server ( instance , prop )
-- For PASS-based authentication, send PASS before greeting the server
if itte.contexts [ instance ] . auth_type == " pass " then
itte.send_command ( itte.contexts [ instance ] . con ,
itte.contexts [ instance ] . cmds.pass . resp )
itte.contexts [ instance ] . state.authed = true
end
-- Greet the server in all auth scenarios except SASL, which greets the
-- server during CAP negotiation.
if ( itte.contexts [ instance ] . auth_type ~= " sasl " ) then
itte.send_command ( itte.contexts [ instance ] . con ,
itte.contexts [ instance ] . cmds.user . resp )
itte.send_command ( itte.contexts [ instance ] . con ,
itte.contexts [ instance ] . cmds.nick . resp )
end
local joined_chans = false
while ( itte.contexts [ instance ] . state.connected ) and ( not joined_chans ) do
local data , status = itte.contexts [ instance ] . con : receive ( )
if itte.contexts [ instance ] . auth_type == " sasl " then
itte.negotiate_cap ( itte.contexts [ instance ] , data )
elseif itte.contexts [ instance ] . auth_type == " nickserv " then
itte.auth_nickserv ( itte.contexts [ instance ] , data )
end
-- Minimum requirements for joining channels: client has authenticated if
-- an auth type is set, or has received a NOTICE [nick] message during an
-- ident lookup.
if itte.contexts [ instance ] . state.authed then
itte.traverse_channels ( itte.contexts [ instance ] , " join " )
joined_chans = true
elseif ( data ~= nil ) then
if util.is_substr ( data , itte.contexts [ instance ] . cmds.join . check )
then
itte.traverse_channels ( itte.contexts [ instance ] , " join " )
joined_chans = true
end
end
end
for name , _ in pairs ( itte.servers ) do
itte.connect_server ( name )
end
-- Add listeners
while itte.state . connected do
for instance , cxt in pairs ( itte.contexts ) do
for _ , cxt in pairs ( itte.contexts ) do
itte.add_listener ( cxt )
end
end