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
|
_ordered_targets = nil, -- ordered list of targets for UI
|
||||||
_current_target = nil, -- active target ("*" for server console)
|
_current_target = nil, -- active target ("*" for server console)
|
||||||
_server_label = nil,
|
_server_label = nil,
|
||||||
|
_unread = nil, -- map target => count of unseen messages
|
||||||
}
|
}
|
||||||
|
|
||||||
function IrcChatView:init()
|
function IrcChatView:init()
|
||||||
@@ -62,6 +63,9 @@ function IrcChatView:init()
|
|||||||
self._ordered_targets = {}
|
self._ordered_targets = {}
|
||||||
self._current_target = nil
|
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._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()
|
self:updateTitle()
|
||||||
TextViewer.init(self)
|
TextViewer.init(self)
|
||||||
-- Start connection after UI init so we can show logs
|
-- Start connection after UI init so we can show logs
|
||||||
@@ -71,7 +75,7 @@ end
|
|||||||
function IrcChatView:updateTitle()
|
function IrcChatView:updateTitle()
|
||||||
local tgt = self._current_target
|
local tgt = self._current_target
|
||||||
if tgt and tgt ~= "*" then
|
if tgt and tgt ~= "*" then
|
||||||
self.title = T(_("%1 — %2"), self._server_label, tgt)
|
self.title = tgt
|
||||||
else
|
else
|
||||||
self.title = self._server_label
|
self.title = self._server_label
|
||||||
end
|
end
|
||||||
@@ -83,6 +87,9 @@ function IrcChatView:ensureBuffer(target)
|
|||||||
self._buffers[target] = ""
|
self._buffers[target] = ""
|
||||||
table.insert(self._ordered_targets, target)
|
table.insert(self._ordered_targets, target)
|
||||||
end
|
end
|
||||||
|
if self._unread[target] == nil then
|
||||||
|
self._unread[target] = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function IrcChatView:switchTarget(target)
|
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.text_widget:setText(self._buffers[target] or "")
|
||||||
self.scroll_text_w:scrollToBottom()
|
self.scroll_text_w:scrollToBottom()
|
||||||
end
|
end
|
||||||
|
-- reset unread counter when focusing this target
|
||||||
|
self._unread[target] = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function IrcChatView:appendLine(line, target)
|
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.text_widget:setText(buf)
|
||||||
self.scroll_text_w:scrollToBottom()
|
self.scroll_text_w:scrollToBottom()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- increment unread counter for background target
|
||||||
|
self._unread[target] = (self._unread[target] or 0) + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -124,7 +136,7 @@ function IrcChatView:startConnection()
|
|||||||
return
|
return
|
||||||
end
|
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')
|
sock:settimeout(10, 't')
|
||||||
local ok, cerr = sock:connect(host, port)
|
local ok, cerr = sock:connect(host, port)
|
||||||
if not ok then
|
if not ok then
|
||||||
@@ -132,8 +144,43 @@ function IrcChatView:startConnection()
|
|||||||
pcall(function() sock:close() end)
|
pcall(function() sock:close() end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
sock:settimeout(0, 'b')
|
local use_tls = (tonumber(port) == 6697) or (self._server.tls == true)
|
||||||
self._sock = sock
|
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)
|
-- Authenticate if configured, then send NICK/USER; Join will be sent after welcome (001)
|
||||||
self._connected = true
|
self._connected = true
|
||||||
@@ -328,13 +375,26 @@ function IrcChatView:handleLine(line)
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
elseif command == "001" then -- welcome (registered)
|
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._registered = true
|
||||||
self:appendLine(msg)
|
self:appendLine(msg)
|
||||||
if self._pending_join and #self._pending_join > 0 then
|
if self._pending_join and #self._pending_join > 0 then
|
||||||
self:sendRaw(string.format("JOIN %s\r\n", self._pending_join))
|
self:sendRaw(string.format("JOIN %s\r\n", self._pending_join))
|
||||||
end
|
end
|
||||||
return
|
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
|
elseif command == "376" or command == "422" then
|
||||||
-- End of MOTD / No MOTD: safe to join if not yet joined
|
-- End of MOTD / No MOTD: safe to join if not yet joined
|
||||||
if not self._registered then self._registered = true end
|
if not self._registered then self._registered = true end
|
||||||
@@ -382,7 +442,14 @@ function IrcChatView:onShowMenu()
|
|||||||
for _, tgt in ipairs(self._ordered_targets) do
|
for _, tgt in ipairs(self._ordered_targets) do
|
||||||
table.insert(buttons, {
|
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,
|
checked_func = function() return self._current_target == tgt end,
|
||||||
callback = function()
|
callback = function()
|
||||||
self:switchTarget(tgt)
|
self:switchTarget(tgt)
|
||||||
|
|||||||
Reference in New Issue
Block a user