diff --git a/asyncoperations.lua b/asyncoperations.lua
index bd0c652..a8ea2dd 100644
--- a/asyncoperations.lua
+++ b/asyncoperations.lua
@@ -1,12 +1,18 @@
local table = table
local assert = assert
+local select = select
module "irc"
local meta = _META
-function meta:send(fmt, ...)
- local bytes, err = self.socket:send(fmt:format(...) .. "\r\n")
+function meta:send(msg, ...)
+ if select("#", ...) > 0 then
+ msg = msg:format(...)
+ end
+ self:invoke("OnSend", msg)
+
+ local bytes, err = self.socket:send(msg .. "\r\n")
if not bytes and err ~= "timeout" and err ~= "wantwrite" then
self:invoke("OnDisconnect", err, true)
diff --git a/doc/irc.luadoc b/doc/irc.luadoc
index 85dc229..c8c5d8e 100644
--- a/doc/irc.luadoc
+++ b/doc/irc.luadoc
@@ -71,9 +71,9 @@ function irc:whois(nick)
function irc:topic(channel)
--- Send a raw line of IRC to the server.
--- @param fmt Line to be sent, excluding newline characters.
--- @param ... Format parameters for fmt
, with string.format
semantics.
-function irc:send(fmt, ...)
+-- @param msg Line to be sent, excluding newline characters.
+-- @param ... Format parameters for msg
, with string.format
semantics. [optional]
+function irc:send(msg, ...)
--- Send a message to a channel or user.
-- @param target Nick or channel to send to.
@@ -125,7 +125,9 @@ function irc:shutdown()
--- List of hooks you can use with irc:hook. The parameter list describes the parameters passed to the callback function.
--
PreRegister(connection)
Useful for CAP commands and SASL.OnRaw(line) - (any non false/nil return value assumes line handled and will not be further processed)
OnSend(line)
OnDisconnect(message, errorOccurred)
OnChat(user, channel, message)
OnNotice(user, channel, message)
OnKick(channel, nick, kicker, reason)
* (kicker is a user
table)OnUserMode(modes)
OnChannelMode(user, channel, modes)
OnModeChange(user, target, modes)
*OnModeChange(user, target, modes, ...)
* ('...' contains mode options such as banmasks)username
- User username.host
- User hostname.realname
- User real name.access
- User access, available in channel-oriented callbacks. Can be '+', '@', and others, depending on the server.access
- User access, available in channel-oriented callbacks. A table containing the boolean fields 'op', 'halfop', and 'voice'.nick
, fields may be missing. To fill them in, enable user tracking and use irc:whois.
-- @name User
diff --git a/handlers.lua b/handlers.lua
index 39d6777..6b892f9 100644
--- a/handlers.lua
+++ b/handlers.lua
@@ -1,6 +1,7 @@
local pairs = pairs
local error = error
local tonumber = tonumber
+local table = table
module "irc"
@@ -83,7 +84,7 @@ handlers["353"] = function(o, prefix, me, chanType, channel, names)
local users = o.channels[channel].users
for nick in names:gmatch("(%S+)") do
local access, name = parseNick(nick)
- users[name] = {type = access}
+ users[name] = {access = access}
end
end
end
@@ -130,8 +131,26 @@ handlers["324"] = function(o, prefix, user, channel, modes)
o:invoke("OnChannelMode", channel, modes)
end
-handlers["MODE"] = function(o, prefix, target, modes)
- o:invoke("OnModeChange", parsePrefix(prefix), target, modes)
+handlers["MODE"] = function(o, prefix, target, modes, ...)
+ if o.track_users and target ~= o.nick then
+ local add = true
+ local optList = {...}
+ for c in modes:gmatch(".") do
+ if c == "+" then add = true
+ elseif c == "-" then add = false
+ elseif c == "o" then
+ local user = table.remove(optList, 1)
+ o.channels[target].users[user].access.op = add
+ elseif c == "h" then
+ local user = table.remove(optList, 1)
+ o.channels[target].users[user].access.halfop = add
+ elseif c == "v" then
+ local user = table.remove(optList, 1)
+ o.channels[target].users[user].access.voice = add
+ end
+ end
+ end
+ o:invoke("OnModeChange", parsePrefix(prefix), target, modes, ...)
end
handlers["ERROR"] = function(o, prefix, message)
diff --git a/init.lua b/init.lua
index 3c8aec3..e94aa24 100644
--- a/init.lua
+++ b/init.lua
@@ -26,7 +26,7 @@ function meta_preconnect.__index(o, k)
local v = rawget(meta_preconnect, k)
if not v and meta[k] then
- error("field '"..k.."' is not accessible before connecting", 2)
+ error(("field '%s' is not accessible before connecting"):format(k), 2)
end
return v
end
@@ -117,6 +117,11 @@ function meta_preconnect:connect(_host, _port)
self.socket = s
setmetatable(self, meta)
+ self:send("CAP REQ multi-prefix")
+
+ self:invoke("PreRegister", self)
+ self:send("CAP END")
+
if password then
self:send("PASS %s", password)
end
diff --git a/util.lua b/util.lua
index 91b9ad4..232ffd6 100644
--- a/util.lua
+++ b/util.lua
@@ -48,17 +48,30 @@ function parse(line)
end
function parseNick(nick)
- return nick:match("^([%+@]?)(.+)$")
+ local access, name = nick:match("^([%+@]*)(.+)$")
+ return parseAccess(access or ""), name
end
function parsePrefix(prefix)
local user = {}
if prefix then
- user.access, user.nick, user.username, user.host = prefix:match("^([%+@]?)(.+)!(.+)@(.+)$")
+ user.access, user.nick, user.username, user.host = prefix:match("^([%+@]*)(.+)!(.+)@(.+)$")
end
+ user.access = parseAccess(user.access or "")
return user
end
+function parseAccess(accessString)
+ local access = {op = false, halfop = false, voice = false}
+ for c in accessString:gmatch(".") do
+ if c == "@" then access.op = true
+ elseif c == "%" then access.halfop = true
+ elseif c == "+" then access.voice = true
+ end
+ end
+ return access
+end
+
--mIRC markup scheme (de-facto standard)
color = {
black = 1,