Bugfixes
- Add global admins table, set disconnect and reload built-in handlers to be accessible by global admins only - Only send an unknown code error in private messages to avoid responding to other bots' codes when there is a prefix collision - Fix server contexts not being updated after a config reload - Fix service codes being improperly parsed in privmsgs when different code prefixes are used - Fix joining channels on some networks when connecting without authentication - Fix disconnecting from one server also causing disconnection from other servers
This commit is contained in:
		
							parent
							
								
									9005c0fba5
								
							
						
					
					
						commit
						7963d7221c
					
				@ -2,6 +2,16 @@
 | 
			
		||||
-- Server configuration
 | 
			
		||||
-- ---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
-- `itte_admins`
 | 
			
		||||
-- Users who can access administrative functions in the client, not limited to
 | 
			
		||||
-- the server instance. The username is for the client only and independent of
 | 
			
		||||
-- IRC network usernames.
 | 
			
		||||
--
 | 
			
		||||
-- This variable is optional, but certain handlers like server disconnection
 | 
			
		||||
-- will not work if it is unset.
 | 
			
		||||
itte_admins = { globaladmin = "password", }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- `itte_servers`
 | 
			
		||||
-- Below is a sample server configuration.
 | 
			
		||||
-- If the server does not support CAP negotiation (used for SASL
 | 
			
		||||
@ -19,6 +29,6 @@ itte_servers = {
 | 
			
		||||
    auth_user = "botuser",
 | 
			
		||||
    auth_pass = "password",
 | 
			
		||||
    code_prefix = "!",
 | 
			
		||||
    admins = { adminuser = "password" },
 | 
			
		||||
    admins = { adminuser = "password", },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										196
									
								
								itte.lua
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								itte.lua
									
									
									
									
									
								
							@ -47,6 +47,9 @@ itte.servers = {}
 | 
			
		||||
-- Store custom handlers
 | 
			
		||||
itte.handlers = {}
 | 
			
		||||
 | 
			
		||||
-- Store global admins
 | 
			
		||||
itte.admins = {}
 | 
			
		||||
 | 
			
		||||
-- Internal only: store core handlers
 | 
			
		||||
itte._h = {}
 | 
			
		||||
 | 
			
		||||
@ -57,8 +60,8 @@ itte.docs._h = {}
 | 
			
		||||
-- Internal only: track application state
 | 
			
		||||
itte.state = {
 | 
			
		||||
  connected = false,
 | 
			
		||||
  connections = {},
 | 
			
		||||
}
 | 
			
		||||
itte.contexts = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- ---------------------------------------------------------------------------
 | 
			
		||||
@ -79,7 +82,7 @@ end
 | 
			
		||||
itte.docs.get_config = [[  ()
 | 
			
		||||
  Load the config file.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.get_config()
 | 
			
		||||
function itte.get_config(reload)
 | 
			
		||||
  -- If prefix is not set, try custom file names or defaults
 | 
			
		||||
  if itte.confs.prefix == nil then
 | 
			
		||||
    util.source_file(itte.confs.config)
 | 
			
		||||
@ -103,6 +106,20 @@ function itte.get_config()
 | 
			
		||||
  end
 | 
			
		||||
  if itte_config ~= nil then itte.config = itte_config 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.
 | 
			
		||||
  if reload then
 | 
			
		||||
    for instance, prop in pairs(itte.servers) do
 | 
			
		||||
      cxt_old = itte.contexts[instance]
 | 
			
		||||
      itte.contexts[instance] = prop
 | 
			
		||||
      itte.contexts[instance].name = instance
 | 
			
		||||
      itte.contexts[instance].cmds = itte.get_commands(prop)
 | 
			
		||||
      itte.contexts[instance].con = cxt_old.con
 | 
			
		||||
      itte.contexts[instance].con:settimeout(0.5)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -153,9 +170,9 @@ function itte.get_commands(svr)
 | 
			
		||||
    },
 | 
			
		||||
    -- Main commands
 | 
			
		||||
    join = {
 | 
			
		||||
      -- 001 is a welcome message
 | 
			
		||||
      -- Cannot join earlier on some servers if not registered
 | 
			
		||||
      check = "001 " .. svr.nick,
 | 
			
		||||
      -- "MODE" seems to be a more reliable check
 | 
			
		||||
      check = "MODE",
 | 
			
		||||
      resp = "JOIN ",
 | 
			
		||||
    },
 | 
			
		||||
    nick = {
 | 
			
		||||
@ -177,7 +194,7 @@ function itte.get_commands(svr)
 | 
			
		||||
      resp = "PONG ",
 | 
			
		||||
    },
 | 
			
		||||
    privmsg = {
 | 
			
		||||
      check = "PRIVMSG(.*):!",
 | 
			
		||||
      check = "PRIVMSG(.*):" .. svr.code_prefix,
 | 
			
		||||
      resp = "PRIVMSG ",
 | 
			
		||||
    },
 | 
			
		||||
    quit = {
 | 
			
		||||
@ -214,14 +231,16 @@ function itte.message(cxt, users, str)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
itte.docs.connect_server = [[  (server_table)
 | 
			
		||||
itte.docs.connect_server = [[  (name_str, server_table)
 | 
			
		||||
  Initialise a socket connection to a server and return a context table.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.connect_server(svr)
 | 
			
		||||
function itte.connect_server(name, svr)
 | 
			
		||||
  -- Load server context
 | 
			
		||||
  local context = svr
 | 
			
		||||
  context.name = name
 | 
			
		||||
  context.cmds = itte.get_commands(svr)
 | 
			
		||||
  context.state = {
 | 
			
		||||
    connected = false,
 | 
			
		||||
    cap_greeted = false,
 | 
			
		||||
    cap_ls = false,
 | 
			
		||||
    cap_checked = false,
 | 
			
		||||
@ -239,16 +258,43 @@ function itte.connect_server(svr)
 | 
			
		||||
    verify = "none",
 | 
			
		||||
    options = "all",
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  util.debug("conn", "Connecting to " .. 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()
 | 
			
		||||
  -- Set a short timeout to be non-blocking
 | 
			
		||||
  context.con:settimeout(0.1)
 | 
			
		||||
 | 
			
		||||
  -- Set context and global states
 | 
			
		||||
  context.state.connected = true
 | 
			
		||||
  itte.state.connected = true
 | 
			
		||||
 | 
			
		||||
  return context
 | 
			
		||||
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.",
 | 
			
		||||
      itte.config.debug)
 | 
			
		||||
  end
 | 
			
		||||
  itte.contexts[name].con:close()
 | 
			
		||||
  itte.contexts[name] = nil
 | 
			
		||||
 | 
			
		||||
  -- 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)
 | 
			
		||||
    itte.state.connected = false
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
itte.docs.negotiate_cap = [[  (context_table, data_str)
 | 
			
		||||
  Negotiate client capabilities and authenticate with SASL.
 | 
			
		||||
  ]]
 | 
			
		||||
@ -291,7 +337,7 @@ function itte.negotiate_cap(cxt, str)
 | 
			
		||||
      cxt.state.authed = true
 | 
			
		||||
      cxt.state.cap_checked = true
 | 
			
		||||
      if (cxt.state.cap_checked) and (not cxt.state.authed) and
 | 
			
		||||
        (itte.config.notify_errors) then
 | 
			
		||||
        (itte.config.notify_errors) and (cxt.admins ~= nil) then
 | 
			
		||||
        itte.send_command(cxt.con, cxt.cmds.cap_end.resp)
 | 
			
		||||
        itte.message(cxt, util.table_keys(cxt.admins),
 | 
			
		||||
          itte.config.errors.sasl_auth_failed)
 | 
			
		||||
@ -315,7 +361,7 @@ function itte.auth_nickserv(cxt, str)
 | 
			
		||||
    elseif util.is_substr(str, cxt.cmds.ns_identify_pass.check) then
 | 
			
		||||
      cxt.state.authed = true
 | 
			
		||||
      if (cxt.state.ns_checked) and (not cxt.state.authed) and
 | 
			
		||||
        (itte.config.notify_errors) then
 | 
			
		||||
        (itte.config.notify_errors) and (cxt.admins ~= nil) then
 | 
			
		||||
        itte.message(cxt, util.table_keys(cxt.admins).
 | 
			
		||||
          itte.config.errors.ns_auth_failed)
 | 
			
		||||
      end
 | 
			
		||||
@ -324,18 +370,29 @@ function itte.auth_nickserv(cxt, str)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
itte.docs.is_admin = [[  (context_table, message_table)
 | 
			
		||||
  Check whether a user is an admin. Return true if the user and password are
 | 
			
		||||
  in the admin table, or false otherwise.
 | 
			
		||||
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
 | 
			
		||||
  if the user and password are in the given admin table, or false otherwise.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.is_admin(cxt, msg)
 | 
			
		||||
  -- No password provided
 | 
			
		||||
  if table.concat(msg.code_params) == "" then
 | 
			
		||||
function itte.is_admin(admins, msg, mode)
 | 
			
		||||
  local in_admins = false
 | 
			
		||||
  -- 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
 | 
			
		||||
    local in_admins = util.is_entry(cxt.admins, msg.sender,
 | 
			
		||||
      msg.code_params[#msg.code_params])
 | 
			
		||||
    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],
 | 
			
		||||
        msg.code_params[#msg.code_params])
 | 
			
		||||
    else
 | 
			
		||||
      -- User is the IRC user, password is suppled in the message
 | 
			
		||||
      in_admins = util.is_entry(admins, msg.sender,
 | 
			
		||||
        msg.code_params[#msg.code_params])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if not in_admins then
 | 
			
		||||
      do return false end
 | 
			
		||||
    else
 | 
			
		||||
@ -352,7 +409,7 @@ itte.docs.notify_no_perms = [[  (context_table, message_table)
 | 
			
		||||
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 then
 | 
			
		||||
  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)
 | 
			
		||||
@ -386,15 +443,22 @@ itte.docs.parse_privmsg = [[  (context_table, data_str)
 | 
			
		||||
  return an associative table of values.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.parse_privmsg(cxt, str)
 | 
			
		||||
  local code_full = string.sub(str, string.find(str, ":" ..  cxt.code_prefix) +
 | 
			
		||||
  1 + string.len(cxt.code_prefix))
 | 
			
		||||
  local code_full = ""
 | 
			
		||||
  -- Separator marks the start of the message body
 | 
			
		||||
  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, ":!") - 2),
 | 
			
		||||
      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)
 | 
			
		||||
@ -416,7 +480,8 @@ function itte.parse_privmsg(cxt, str)
 | 
			
		||||
    ", recipient: " .. msg.recipient ..
 | 
			
		||||
    ", reply_to: " .. msg.reply_to ..
 | 
			
		||||
    ", code: " .. msg.code ..
 | 
			
		||||
    ", code_params: " .. table.concat(msg.code_params) ..
 | 
			
		||||
    ", code_params: " .. table.concat(msg.code_params, " ") ..
 | 
			
		||||
    ", body: " .. msg.body ..
 | 
			
		||||
    " }", itte.config.debug)
 | 
			
		||||
  return msg
 | 
			
		||||
end
 | 
			
		||||
@ -435,7 +500,7 @@ function itte._h.help(cxt, msg)
 | 
			
		||||
  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, msg) then
 | 
			
		||||
  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, ", " ..
 | 
			
		||||
@ -450,7 +515,7 @@ itte.docs._h.join = [[  (context_table, message_table)
 | 
			
		||||
  Join specified channels.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte._h.join(cxt, msg)
 | 
			
		||||
  if not itte.is_admin(cxt, msg) then
 | 
			
		||||
  if not itte.is_admin(cxt.admins, msg) then
 | 
			
		||||
    itte.notify_no_perms(cxt, msg)
 | 
			
		||||
    do return end
 | 
			
		||||
  end
 | 
			
		||||
@ -465,7 +530,7 @@ itte.docs._h.part = [[  (context_table, message_table)
 | 
			
		||||
  Leave specified channels.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte._h.part(cxt, msg)
 | 
			
		||||
  if not itte.is_admin(cxt, msg) then
 | 
			
		||||
  if not itte.is_admin(cxt.admins, msg) then
 | 
			
		||||
    itte.notify_no_perms(cxt, msg)
 | 
			
		||||
    do return end
 | 
			
		||||
  end
 | 
			
		||||
@ -488,7 +553,7 @@ itte.docs._h.quit = [[  (context_table, message_table)
 | 
			
		||||
  Disconnect from the server.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte._h.quit(cxt, msg)
 | 
			
		||||
  if not itte.is_admin(cxt, msg) then
 | 
			
		||||
  if not itte.is_admin(itte.admins, msg, "global") then
 | 
			
		||||
    itte.notify_no_perms(cxt, msg)
 | 
			
		||||
    do return end
 | 
			
		||||
  end
 | 
			
		||||
@ -500,11 +565,11 @@ itte.docs._h.reload = [[  (context_table, message_table)
 | 
			
		||||
  Reload the server config.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte._h.reload(cxt, msg)
 | 
			
		||||
  if not itte.is_admin(cxt, msg) then
 | 
			
		||||
  if not itte.is_admin(itte.admins, msg, "global") then
 | 
			
		||||
    itte.notify_no_perms(cxt, msg)
 | 
			
		||||
    do return end
 | 
			
		||||
  end
 | 
			
		||||
  itte.get_config()
 | 
			
		||||
  itte.get_config(true)
 | 
			
		||||
  itte.message(cxt, { msg.reply_to }, itte.config.messages.reload)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -513,13 +578,15 @@ end
 | 
			
		||||
-- Service code mapping and runtime
 | 
			
		||||
-- ---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
itte.docs.listen = [[  (context_table, data_str)
 | 
			
		||||
itte.docs.listen = [[  (name_str, data_str)
 | 
			
		||||
  Parse the socket data string and trigger a response to the server if the
 | 
			
		||||
  string matches preset patterns.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.listen(cxt, str)
 | 
			
		||||
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
 | 
			
		||||
    itte.send_command(cxt.con, string.gsub(str, cxt.cmds.ping.check,
 | 
			
		||||
@ -536,7 +603,11 @@ function itte.listen(cxt, str)
 | 
			
		||||
    elseif util.has_key(itte.handlers, msg.code) then
 | 
			
		||||
      itte.handlers[msg.code](cxt, msg)
 | 
			
		||||
    else
 | 
			
		||||
      itte.message(cxt, { msg.reply_to }, itte.config.errors.unknown_code)
 | 
			
		||||
      -- 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
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -548,22 +619,12 @@ itte.docs.add_listener = [[  (context_table)
 | 
			
		||||
  set `state.connected` to false to activate a client exit.
 | 
			
		||||
  ]]
 | 
			
		||||
function itte.add_listener(cxt)
 | 
			
		||||
  -- Set a short timeout to be non-blocking
 | 
			
		||||
  cxt.con:settimeout(0.5)
 | 
			
		||||
  local data, status = cxt.con:receive()
 | 
			
		||||
  if data ~= nil then
 | 
			
		||||
    itte.listen(cxt, data)
 | 
			
		||||
    itte.listen(cxt.name, data)
 | 
			
		||||
 | 
			
		||||
  elseif status == "closed" then
 | 
			
		||||
    util.debug("conn", "Connection " .. status, itte.config.debug)
 | 
			
		||||
    cxt.con:close()
 | 
			
		||||
    itte.state.connections[cxt.name] = nil
 | 
			
		||||
    -- Check if it is the last connection and trigger client exit
 | 
			
		||||
    -- if no connections remain.
 | 
			
		||||
    if #itte.state.connections == 0 then
 | 
			
		||||
      util.debug("conn", "Exiting ...", itte.config.debug)
 | 
			
		||||
      itte.state.connected = false
 | 
			
		||||
    end
 | 
			
		||||
    itte.disconnect_server(cxt.name)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -575,50 +636,47 @@ function itte.run()
 | 
			
		||||
  itte.get_config()
 | 
			
		||||
 | 
			
		||||
  -- Connect to servers and get context with socket ref for each server
 | 
			
		||||
  local contexts = {}
 | 
			
		||||
  itte.contexts = {}
 | 
			
		||||
  for instance, prop in pairs(itte.servers) do
 | 
			
		||||
    contexts[instance] = itte.connect_server(prop)
 | 
			
		||||
    contexts[instance].name = instance
 | 
			
		||||
    itte.state.connected = true
 | 
			
		||||
    itte.state.connections[instance] = true
 | 
			
		||||
    contexts[instance].con:settimeout(1)
 | 
			
		||||
    itte.contexts[instance] = itte.connect_server(instance, prop)
 | 
			
		||||
 | 
			
		||||
    -- For PASS-based authentication, send PASS before greeting the server
 | 
			
		||||
    if contexts[instance].auth_type == "pass" then
 | 
			
		||||
      itte.send_command(contexts[instance].con,
 | 
			
		||||
        contexts[instance].cmds.pass.resp)
 | 
			
		||||
      contexts[instance].state.authed = true
 | 
			
		||||
    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 (contexts[instance].auth_type ~= "sasl") then
 | 
			
		||||
      itte.send_command(contexts[instance].con,
 | 
			
		||||
        contexts[instance].cmds.user.resp)
 | 
			
		||||
      itte.send_command(contexts[instance].con,
 | 
			
		||||
        contexts[instance].cmds.nick.resp)
 | 
			
		||||
    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.state.connections[instance]) and (not joined_chans) do
 | 
			
		||||
      local data, status = contexts[instance].con:receive()
 | 
			
		||||
    while (itte.contexts[instance].state.connected) and (not joined_chans) do
 | 
			
		||||
      local data, status = itte.contexts[instance].con:receive()
 | 
			
		||||
 | 
			
		||||
      if contexts[instance].auth_type == "sasl" then
 | 
			
		||||
        itte.negotiate_cap(contexts[instance], data)
 | 
			
		||||
      if itte.contexts[instance].auth_type == "sasl" then
 | 
			
		||||
        itte.negotiate_cap(itte.contexts[instance], data)
 | 
			
		||||
 | 
			
		||||
      elseif contexts[instance].auth_type == "nickserv" then
 | 
			
		||||
        itte.auth_nickserv(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 contexts[instance].state.authed then
 | 
			
		||||
        itte.traverse_channels(contexts[instance], "join")
 | 
			
		||||
      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, contexts[instance].cmds.join.check) then
 | 
			
		||||
          itte.traverse_channels(contexts[instance], "join")
 | 
			
		||||
        if util.is_substr(data, itte.contexts[instance].cmds.join.check)
 | 
			
		||||
          then
 | 
			
		||||
          itte.traverse_channels(itte.contexts[instance], "join")
 | 
			
		||||
          joined_chans = true
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -627,7 +685,7 @@ function itte.run()
 | 
			
		||||
 | 
			
		||||
  -- Add listeners
 | 
			
		||||
  while itte.state.connected do
 | 
			
		||||
    for instance, cxt in pairs(contexts) do
 | 
			
		||||
    for instance, cxt in pairs(itte.contexts) do
 | 
			
		||||
      itte.add_listener(cxt)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user