irc: multi-channel buffers, unread badges, TLS, nick sync and fixes

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-09-20 01:40:20 +03:00
parent 636d71a15f
commit 736441c2e1

View File

@@ -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)