irc: multi-channel buffers, unread badges, TLS, nick sync and fixes
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
79
main.lua
79
main.lua
@@ -37,6 +37,7 @@ local IrcChatView = TextViewer:extend{
|
||||
_ordered_targets = nil, -- ordered list of targets for UI
|
||||
_current_target = nil, -- active target ("*" for server console)
|
||||
_server_label = nil,
|
||||
_unread = nil, -- map target => count of unseen messages
|
||||
}
|
||||
|
||||
function IrcChatView:init()
|
||||
@@ -62,6 +63,9 @@ function IrcChatView:init()
|
||||
self._ordered_targets = {}
|
||||
self._current_target = nil
|
||||
self._server_label = self._server and (self._server.name or (self._server.host .. ":" .. tostring(self._server.port or 6667))) or "IRC"
|
||||
self._unread = {}
|
||||
-- default to server console target
|
||||
self:switchTarget("*")
|
||||
self:updateTitle()
|
||||
TextViewer.init(self)
|
||||
-- Start connection after UI init so we can show logs
|
||||
@@ -71,7 +75,7 @@ end
|
||||
function IrcChatView:updateTitle()
|
||||
local tgt = self._current_target
|
||||
if tgt and tgt ~= "*" then
|
||||
self.title = T(_("%1 — %2"), self._server_label, tgt)
|
||||
self.title = tgt
|
||||
else
|
||||
self.title = self._server_label
|
||||
end
|
||||
@@ -83,6 +87,9 @@ function IrcChatView:ensureBuffer(target)
|
||||
self._buffers[target] = ""
|
||||
table.insert(self._ordered_targets, target)
|
||||
end
|
||||
if self._unread[target] == nil then
|
||||
self._unread[target] = 0
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:switchTarget(target)
|
||||
@@ -94,6 +101,8 @@ function IrcChatView:switchTarget(target)
|
||||
self.scroll_text_w.text_widget:setText(self._buffers[target] or "")
|
||||
self.scroll_text_w:scrollToBottom()
|
||||
end
|
||||
-- reset unread counter when focusing this target
|
||||
self._unread[target] = 0
|
||||
end
|
||||
|
||||
function IrcChatView:appendLine(line, target)
|
||||
@@ -109,6 +118,9 @@ function IrcChatView:appendLine(line, target)
|
||||
self.scroll_text_w.text_widget:setText(buf)
|
||||
self.scroll_text_w:scrollToBottom()
|
||||
end
|
||||
else
|
||||
-- increment unread counter for background target
|
||||
self._unread[target] = (self._unread[target] or 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -124,7 +136,7 @@ function IrcChatView:startConnection()
|
||||
return
|
||||
end
|
||||
|
||||
-- Do a blocking connect with a short timeout, then switch to non-blocking.
|
||||
-- Do a blocking connect with a short timeout, then switch to non-blocking (and TLS if needed).
|
||||
sock:settimeout(10, 't')
|
||||
local ok, cerr = sock:connect(host, port)
|
||||
if not ok then
|
||||
@@ -132,8 +144,43 @@ function IrcChatView:startConnection()
|
||||
pcall(function() sock:close() end)
|
||||
return
|
||||
end
|
||||
sock:settimeout(0, 'b')
|
||||
self._sock = sock
|
||||
local use_tls = (tonumber(port) == 6697) or (self._server.tls == true)
|
||||
if use_tls then
|
||||
local okreq, ssl = pcall(require, "ssl")
|
||||
if not okreq or not ssl then
|
||||
self:appendLine(_("TLS requested but LuaSec is unavailable."))
|
||||
sock:settimeout(0, 'b')
|
||||
self._sock = sock
|
||||
else
|
||||
local params = {
|
||||
mode = "client",
|
||||
protocol = "tlsv1_2",
|
||||
verify = "none",
|
||||
options = "all",
|
||||
-- SNI support
|
||||
server = host,
|
||||
}
|
||||
local ssock, werr = ssl.wrap(sock, params)
|
||||
if not ssock then
|
||||
self:appendLine(T(_("TLS wrap failed: %1"), tostring(werr)))
|
||||
sock:settimeout(0, 'b')
|
||||
self._sock = sock
|
||||
else
|
||||
ssock:settimeout(10, 't')
|
||||
local hok, herr = ssock:dohandshake()
|
||||
if not hok then
|
||||
self:appendLine(T(_("TLS handshake failed: %1"), tostring(herr)))
|
||||
pcall(function() ssock:close() end)
|
||||
return
|
||||
end
|
||||
ssock:settimeout(0, 'b')
|
||||
self._sock = ssock
|
||||
end
|
||||
end
|
||||
else
|
||||
sock:settimeout(0, 'b')
|
||||
self._sock = sock
|
||||
end
|
||||
|
||||
-- Authenticate if configured, then send NICK/USER; Join will be sent after welcome (001)
|
||||
self._connected = true
|
||||
@@ -328,13 +375,26 @@ function IrcChatView:handleLine(line)
|
||||
end
|
||||
return
|
||||
elseif command == "001" then -- welcome (registered)
|
||||
local msg = rest:match("^%S+%s+:(.+)$") or rest
|
||||
local target_nick, msg = rest:match("^(%S+)%s+:(.+)$")
|
||||
if target_nick then
|
||||
self._nick = target_nick
|
||||
else
|
||||
msg = rest
|
||||
end
|
||||
self._registered = true
|
||||
self:appendLine(msg)
|
||||
if self._pending_join and #self._pending_join > 0 then
|
||||
self:sendRaw(string.format("JOIN %s\r\n", self._pending_join))
|
||||
end
|
||||
return
|
||||
elseif command == "NICK" then -- someone changed nick; update ours if it's us
|
||||
local newnick = rest:match("^:(.+)$") or rest
|
||||
local oldnick = prefix:match("^([^!]+)!") or prefix
|
||||
if oldnick == self._nick and newnick and #newnick > 0 then
|
||||
self._nick = newnick
|
||||
self:appendLine(T(_("You are now known as %1"), newnick))
|
||||
end
|
||||
return
|
||||
elseif command == "376" or command == "422" then
|
||||
-- End of MOTD / No MOTD: safe to join if not yet joined
|
||||
if not self._registered then self._registered = true end
|
||||
@@ -382,7 +442,14 @@ function IrcChatView:onShowMenu()
|
||||
for _, tgt in ipairs(self._ordered_targets) do
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = tgt,
|
||||
text_func = function()
|
||||
local n = self._unread[tgt] or 0
|
||||
if n > 0 then
|
||||
return string.format("%s (%d)", tgt, n)
|
||||
else
|
||||
return tgt
|
||||
end
|
||||
end,
|
||||
checked_func = function() return self._current_target == tgt end,
|
||||
callback = function()
|
||||
self:switchTarget(tgt)
|
||||
|
||||
Reference in New Issue
Block a user