769
main.lua
769
main.lua
@@ -0,0 +1,769 @@
|
||||
-- IRC client plugin for KOReader
|
||||
-- Implements a simple IRC client using LuaSocket.
|
||||
-- Provides a submenu with Server list & Username, and a basic chat view.
|
||||
|
||||
local DataStorage = require("datastorage")
|
||||
local Dispatcher = require("dispatcher")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local InputDialog = require("ui/widget/inputdialog")
|
||||
local LuaSettings = require("luasettings")
|
||||
local MultiInputDialog = require("ui/widget/multiinputdialog")
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local TextViewer = require("ui/widget/textviewer")
|
||||
local NetworkMgr = require("ui/network/manager")
|
||||
local socket = require("socket")
|
||||
local socketutil = require("socketutil")
|
||||
local _ = require("gettext")
|
||||
local T = require("ffi/util").template
|
||||
|
||||
-- Chat view based on TextViewer for scrollable text & menu buttons
|
||||
local IrcChatView = TextViewer:extend{
|
||||
title = _("IRC Chat"),
|
||||
text = "",
|
||||
add_default_buttons = true,
|
||||
monospace_font = true,
|
||||
text_type = "code",
|
||||
_connected = false,
|
||||
_closing = false,
|
||||
_receive_task = nil,
|
||||
_sock = nil,
|
||||
_server = nil, -- {name, host, port}
|
||||
_channel = nil, -- optional starting channel
|
||||
_nick = nil,
|
||||
-- Multi-target buffers
|
||||
_buffers = nil, -- map target => text
|
||||
_ordered_targets = nil, -- ordered list of targets for UI
|
||||
_current_target = nil, -- active target ("*" for server console)
|
||||
_server_label = nil,
|
||||
}
|
||||
|
||||
function IrcChatView:init()
|
||||
-- Buttons: Send, Close
|
||||
self.buttons_table = {
|
||||
{
|
||||
{
|
||||
text = _("Send"),
|
||||
callback = function()
|
||||
self:promptSendMessage()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Leave"),
|
||||
callback = function()
|
||||
self:onClose()
|
||||
end,
|
||||
},
|
||||
}
|
||||
}
|
||||
-- Buffers & title
|
||||
self._buffers = {}
|
||||
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:updateTitle()
|
||||
TextViewer.init(self)
|
||||
-- Start connection after UI init so we can show logs
|
||||
UIManager:nextTick(function() self:startConnection() end)
|
||||
end
|
||||
|
||||
function IrcChatView:updateTitle()
|
||||
local tgt = self._current_target
|
||||
if tgt and tgt ~= "*" then
|
||||
self.title = T(_("%1 — %2"), self._server_label, tgt)
|
||||
else
|
||||
self.title = self._server_label
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:ensureBuffer(target)
|
||||
target = target or "*"
|
||||
if not self._buffers[target] then
|
||||
self._buffers[target] = ""
|
||||
table.insert(self._ordered_targets, target)
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:switchTarget(target)
|
||||
if not target then return end
|
||||
self:ensureBuffer(target)
|
||||
self._current_target = target
|
||||
self:updateTitle()
|
||||
if self.scroll_text_w and self.scroll_text_w.text_widget then
|
||||
self.scroll_text_w.text_widget:setText(self._buffers[target] or "")
|
||||
self.scroll_text_w:scrollToBottom()
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:appendLine(line, target)
|
||||
if not line or line == "" then return end
|
||||
target = target or self._current_target or "*"
|
||||
self:ensureBuffer(target)
|
||||
local prefix = os.date("[%H:%M]") .. " "
|
||||
local buf = self._buffers[target]
|
||||
buf = (buf and #buf > 0) and (buf .. "\n" .. prefix .. line) or (prefix .. line)
|
||||
self._buffers[target] = buf
|
||||
if target == (self._current_target or "*") then
|
||||
if self.scroll_text_w and self.scroll_text_w.text_widget then
|
||||
self.scroll_text_w.text_widget:setText(buf)
|
||||
self.scroll_text_w:scrollToBottom()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:startConnection()
|
||||
if self._connected or self._closing then return end
|
||||
local host = self._server.host
|
||||
local port = tonumber(self._server.port) or 6667
|
||||
self:appendLine(T(_("Connecting to %1:%2…"), host, tostring(port)))
|
||||
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then
|
||||
self:appendLine(T(_("Socket error: %1"), tostring(err)))
|
||||
return
|
||||
end
|
||||
|
||||
-- Do a blocking connect with a short timeout, then switch to non-blocking.
|
||||
sock:settimeout(10, 't')
|
||||
local ok, cerr = sock:connect(host, port)
|
||||
if not ok then
|
||||
self:appendLine(T(_("Connect failed: %1"), tostring(cerr)))
|
||||
pcall(function() sock:close() end)
|
||||
return
|
||||
end
|
||||
sock:settimeout(0, 'b')
|
||||
self._sock = sock
|
||||
|
||||
-- Authenticate if configured, then send NICK/USER; Join will be sent after welcome (001)
|
||||
self._connected = true
|
||||
self._registered = false
|
||||
self._pending_join = self._channel
|
||||
self:appendLine(T(_("Logging in as %1…"), self._nick))
|
||||
-- PASS MUST be sent before NICK/USER if needed (e.g., ZNC)
|
||||
local auth_user = self._server.auth_user
|
||||
local auth_pass = self._server.auth_pass
|
||||
if auth_user and auth_pass then
|
||||
self:sendRaw(string.format("PASS %s:%s\r\n", auth_user, auth_pass))
|
||||
elseif auth_pass and #auth_pass > 0 then
|
||||
-- plain server password
|
||||
self:sendRaw(string.format("PASS %s\r\n", auth_pass))
|
||||
end
|
||||
self:sendRaw(string.format("NICK %s\r\n", self._nick))
|
||||
self:sendRaw(string.format("USER %s 0 * :%s\r\n", self._nick, self._nick))
|
||||
|
||||
-- Schedule receive loop
|
||||
self._receive_task = function() self:receiveLoop() end
|
||||
UIManager:scheduleIn(0.2, self._receive_task)
|
||||
end
|
||||
|
||||
function IrcChatView:sendRaw(line)
|
||||
if not self._sock then return end
|
||||
-- Best effort write
|
||||
pcall(function()
|
||||
self._sock:send(line)
|
||||
end)
|
||||
end
|
||||
|
||||
function IrcChatView:sendMessage(msg)
|
||||
if not msg or msg == "" then return end
|
||||
if msg:sub(1,1) == "/" then
|
||||
-- Simple slash commands: /join, /part, /me, /msg
|
||||
local parts = {}
|
||||
for w in msg:gmatch("%S+") do table.insert(parts, w) end
|
||||
local cmd = parts[1]:sub(2):lower()
|
||||
if cmd == "join" and parts[2] then
|
||||
local ch = parts[2]
|
||||
self:sendRaw(string.format("JOIN %s\r\n", ch))
|
||||
self:appendLine(T(_("Joining %1"), ch), ch)
|
||||
self:ensureBuffer(ch)
|
||||
self:switchTarget(ch)
|
||||
elseif cmd == "part" then
|
||||
local ch = parts[2] or self._current_target
|
||||
if ch then
|
||||
self:sendRaw(string.format("PART %s\r\n", ch))
|
||||
self:appendLine(T(_("Leaving %1"), ch), ch)
|
||||
end
|
||||
elseif cmd == "me" and parts[2] then
|
||||
if self._current_target and self._current_target ~= "*" then
|
||||
local action = msg:match("/me%s+(.+)$") or ""
|
||||
self:sendRaw(string.format("PRIVMSG %s :\u{0001}ACTION %s\u{0001}\r\n", self._current_target, action))
|
||||
self:appendLine(T(_("* %1 %2"), self._nick, action), self._current_target)
|
||||
end
|
||||
elseif cmd == "msg" and parts[2] and parts[3] then
|
||||
local target = parts[2]
|
||||
local text = msg:match("/msg%s+%S+%s+(.+)$") or ""
|
||||
self:sendRaw(string.format("PRIVMSG %s :%s\r\n", target, text))
|
||||
self:appendLine(T(_("→ [%1] %2"), target, text), target)
|
||||
else
|
||||
self:appendLine(_("Unknown command."))
|
||||
end
|
||||
return
|
||||
end
|
||||
if self._current_target and self._current_target ~= "*" then
|
||||
self:sendRaw(string.format("PRIVMSG %s :%s\r\n", self._current_target, msg))
|
||||
self:appendLine(T(_("<%1> %2"), self._nick, msg), self._current_target)
|
||||
else
|
||||
self:appendLine(_("No channel joined. Use /join #channel"))
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:promptSendMessage()
|
||||
local dialog
|
||||
dialog = InputDialog:new{
|
||||
title = _("Send message"),
|
||||
input = "",
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Send"),
|
||||
is_default = true,
|
||||
callback = function()
|
||||
local txt = dialog:getInputText()
|
||||
UIManager:close(dialog)
|
||||
self:sendMessage(txt)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Cancel"),
|
||||
callback = function()
|
||||
UIManager:close(dialog)
|
||||
end,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
UIManager:show(dialog)
|
||||
dialog:onShowKeyboard(true)
|
||||
end
|
||||
|
||||
function IrcChatView:receiveLoop()
|
||||
if self._closing then return end
|
||||
if not self._sock then return end
|
||||
-- Non-blocking receive lines; iterate until no more data
|
||||
for _ = 1, 50 do -- cap per tick
|
||||
local line, err, partial = self._sock:receive("*l")
|
||||
if line then
|
||||
self:handleLine(line)
|
||||
elseif err == "timeout" then
|
||||
break
|
||||
elseif err == "closed" then
|
||||
self:appendLine(_("Disconnected."))
|
||||
self._closing = true
|
||||
pcall(function() self._sock:close() end)
|
||||
self._sock = nil
|
||||
break
|
||||
else
|
||||
if partial and #partial > 0 then
|
||||
self:handleLine(partial)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if not self._closing then
|
||||
UIManager:scheduleIn(0.3, self._receive_task)
|
||||
end
|
||||
end
|
||||
|
||||
function IrcChatView:handleLine(line)
|
||||
-- Handle PING/PONG and display messages
|
||||
-- Raw IRC line: ":prefix COMMAND params :trailing" or "PING :token"
|
||||
if line:match("^PING%s*:") then
|
||||
local token = line:match("^PING%s*:(.+)$") or ""
|
||||
self:sendRaw("PONG :" .. token .. "\r\n")
|
||||
return
|
||||
end
|
||||
|
||||
local prefix, command, rest = line:match("^:([^%s]+)%s+(%S+)%s+(.+)$")
|
||||
if command then
|
||||
command = command:upper()
|
||||
if command == "PRIVMSG" then
|
||||
local target, msg = rest:match("^(%S+)%s+:(.+)$")
|
||||
if target and msg then
|
||||
local nick = prefix:match("^([^!]+)!") or prefix
|
||||
if target:sub(1,1) == "#" then
|
||||
self:appendLine(string.format("<%s> %s", nick, msg), target)
|
||||
else
|
||||
-- PM to us
|
||||
self:appendLine(string.format("[%s -> you] %s", nick, msg), nick)
|
||||
end
|
||||
return
|
||||
end
|
||||
elseif command == "NOTICE" then
|
||||
local target, msg = rest:match("^(%S+)%s+:(.+)$")
|
||||
if msg then
|
||||
local nick = prefix and (prefix:match("^([^!]+)!") or prefix) or "*"
|
||||
self:appendLine(string.format("-%s- %s", nick, msg))
|
||||
return
|
||||
end
|
||||
elseif command == "JOIN" then
|
||||
local nick = prefix:match("^([^!]+)!") or prefix
|
||||
local ch = rest:match("^:(.+)$") or rest
|
||||
if ch and ch ~= "" then
|
||||
self:appendLine(string.format("* %s joined %s", nick, ch), ch)
|
||||
if nick == self._nick then
|
||||
self:ensureBuffer(ch)
|
||||
if not self._current_target then
|
||||
self:switchTarget(ch)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
elseif command == "PART" then
|
||||
local nick = prefix:match("^([^!]+)!") or prefix
|
||||
local ch, msg = rest:match("^(%S+)%s*:?(.*)$")
|
||||
if ch then
|
||||
self:appendLine(string.format("* %s left %s %s", nick, ch, msg or ""), ch)
|
||||
end
|
||||
return
|
||||
elseif command == "QUIT" then
|
||||
local nick = prefix:match("^([^!]+)!") or prefix
|
||||
local msg = rest:match("^:(.+)$") or ""
|
||||
self:appendLine(string.format("* %s quit %s", nick, msg))
|
||||
return
|
||||
elseif command == "353" then -- RPL_NAMREPLY
|
||||
local nickmode, chan, names = rest:match("^(%S+)%s+=%s+(%S+)%s+:(.+)$")
|
||||
if names then
|
||||
self:appendLine(T(_("Users: %1"), names), chan)
|
||||
end
|
||||
return
|
||||
elseif command == "001" then -- welcome (registered)
|
||||
local msg = rest:match("^%S+%s+:(.+)$") or rest
|
||||
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 == "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
|
||||
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 == "433" then -- ERR_NICKNAMEINUSE
|
||||
self:appendLine(_("Nickname in use. Try changing Username in menu."))
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Fallback: show raw line trimmed
|
||||
self:appendLine((line:gsub("\r", "")))
|
||||
end
|
||||
|
||||
function IrcChatView:onClose()
|
||||
if self._closing then return end
|
||||
self._closing = true
|
||||
if self._receive_task then
|
||||
UIManager:unschedule(self._receive_task)
|
||||
self._receive_task = nil
|
||||
end
|
||||
if self._sock then
|
||||
pcall(function()
|
||||
if self._current_target and self._current_target:sub(1,1) == "#" then
|
||||
self:sendRaw(string.format("PART %s\r\n", self._current_target))
|
||||
end
|
||||
self:sendRaw("QUIT :bye\r\n")
|
||||
self._sock:close()
|
||||
end)
|
||||
self._sock = nil
|
||||
end
|
||||
TextViewer.onClose(self)
|
||||
end
|
||||
|
||||
-- Extend the hamburger menu to add a list of joined channels/targets.
|
||||
function IrcChatView:onShowMenu()
|
||||
local ButtonDialog = require("ui/widget/buttondialog")
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local buttons = {}
|
||||
|
||||
-- Channels/Targets section
|
||||
if self._ordered_targets and #self._ordered_targets > 0 then
|
||||
for _, tgt in ipairs(self._ordered_targets) do
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = tgt,
|
||||
checked_func = function() return self._current_target == tgt end,
|
||||
callback = function()
|
||||
self:switchTarget(tgt)
|
||||
end,
|
||||
},
|
||||
})
|
||||
end
|
||||
-- separator row
|
||||
table.insert(buttons, { { text = "—", enabled = false, callback = function() end } })
|
||||
end
|
||||
|
||||
-- Font size
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text_func = function()
|
||||
return T(_("Font size: %1"), self.text_font_size)
|
||||
end,
|
||||
align = "left",
|
||||
callback = function()
|
||||
local widget = SpinWidget:new{
|
||||
title_text = _("Font size"),
|
||||
value = self.text_font_size,
|
||||
value_min = 12,
|
||||
value_max = 30,
|
||||
default_value = self.monospace_font and 16 or 20,
|
||||
keep_shown_on_apply = true,
|
||||
callback = function(spin)
|
||||
self.text_font_size = spin.value
|
||||
self:reinit()
|
||||
end,
|
||||
}
|
||||
UIManager:show(widget)
|
||||
end,
|
||||
}
|
||||
})
|
||||
|
||||
-- Monospace toggle
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = _("Monospace font"),
|
||||
checked_func = function()
|
||||
return self.monospace_font
|
||||
end,
|
||||
align = "left",
|
||||
callback = function()
|
||||
self.monospace_font = not self.monospace_font
|
||||
self:reinit()
|
||||
end,
|
||||
}
|
||||
})
|
||||
|
||||
-- Justify toggle
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = _("Justify"),
|
||||
checked_func = function()
|
||||
return self.justified
|
||||
end,
|
||||
align = "left",
|
||||
callback = function()
|
||||
self.justified = not self.justified
|
||||
self:reinit()
|
||||
end,
|
||||
}
|
||||
})
|
||||
|
||||
local dialog = ButtonDialog:new{
|
||||
shrink_unneeded_width = true,
|
||||
buttons = buttons,
|
||||
anchor = function()
|
||||
return self.titlebar.left_button.image.dimen
|
||||
end,
|
||||
}
|
||||
UIManager:show(dialog)
|
||||
end
|
||||
|
||||
-- Main plugin
|
||||
local IRC = WidgetContainer:extend{
|
||||
name = "irc",
|
||||
fullname = _("IRC client"),
|
||||
is_doc_only = false,
|
||||
settings_file = DataStorage:getSettingsDir() .. "/irc_client.lua",
|
||||
settings = nil,
|
||||
servers = nil, -- array of { name, host, port, channels = {"#chan"} }
|
||||
username = nil,
|
||||
}
|
||||
|
||||
function IRC:onDispatcherRegisterActions()
|
||||
Dispatcher:registerAction("irc_open", {category="none", event="OpenIRC", title=self.fullname, general=true})
|
||||
end
|
||||
|
||||
function IRC:init()
|
||||
self:onDispatcherRegisterActions()
|
||||
self:loadSettings()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
function IRC:loadSettings()
|
||||
if self.settings then return end
|
||||
self.settings = LuaSettings:open(self.settings_file)
|
||||
self.username = self.settings:readSetting("username") or os.getenv("USER") or "koreader"
|
||||
self.servers = self.settings:readSetting("servers") or {}
|
||||
end
|
||||
|
||||
function IRC:onFlushSettings()
|
||||
if not self.settings then return end
|
||||
self.settings:saveSetting("username", self.username)
|
||||
self.settings:saveSetting("servers", self.servers)
|
||||
self.settings:flush()
|
||||
end
|
||||
|
||||
function IRC:addToMainMenu(menu_items)
|
||||
menu_items.irc_client = {
|
||||
text = self.fullname,
|
||||
sorting_hint = "more_tools",
|
||||
sub_item_table_func = function()
|
||||
return self:getRootMenuItems()
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function IRC:getRootMenuItems()
|
||||
local items = {
|
||||
{
|
||||
text = _("Server list"),
|
||||
keep_menu_open = true,
|
||||
sub_item_table_func = function()
|
||||
return self:getServerListItems()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text_func = function()
|
||||
return T(_("Default username: %1"), self.username)
|
||||
end,
|
||||
callback = function()
|
||||
self:promptUsername()
|
||||
end,
|
||||
},
|
||||
}
|
||||
return items
|
||||
end
|
||||
|
||||
function IRC:getServerDisplay(server)
|
||||
local label = server.name and #server.name > 0 and server.name or (server.host or "?")
|
||||
local port = server.port and tostring(server.port) or "6667"
|
||||
return string.format("%s (%s:%s)", label, server.host or "?", port)
|
||||
end
|
||||
|
||||
function IRC:getServerListItems()
|
||||
local items = {}
|
||||
for idx, server in ipairs(self.servers) do
|
||||
table.insert(items, {
|
||||
text = self:getServerDisplay(server),
|
||||
sub_item_table = self:getServerActions(server, idx),
|
||||
})
|
||||
end
|
||||
table.insert(items, { separator = true })
|
||||
table.insert(items, {
|
||||
text = _("Add server…"),
|
||||
callback = function()
|
||||
self:promptAddServer()
|
||||
end,
|
||||
})
|
||||
return items
|
||||
end
|
||||
|
||||
function IRC:getServerActions(server, index)
|
||||
local actions = {
|
||||
{
|
||||
text = _("Connect"),
|
||||
callback = function()
|
||||
self:connectToServer(server)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Edit"),
|
||||
callback = function()
|
||||
self:promptEditServer(server, index)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Delete"),
|
||||
callback = function()
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(_("Delete %1?"), self:getServerDisplay(server)),
|
||||
ok_text = _("Delete"),
|
||||
ok_callback = function()
|
||||
table.remove(self.servers, index)
|
||||
self:onFlushSettings()
|
||||
UIManager:show(InfoMessage:new{ text = _("Server removed."), timeout = 2 })
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
}
|
||||
return actions
|
||||
end
|
||||
|
||||
function IRC:promptUsername()
|
||||
local dialog
|
||||
dialog = InputDialog:new{
|
||||
title = _("Username"),
|
||||
input = self.username or "",
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Save"),
|
||||
is_default = true,
|
||||
callback = function()
|
||||
self.username = dialog:getInputText()
|
||||
self:onFlushSettings()
|
||||
UIManager:close(dialog)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Cancel"),
|
||||
callback = function()
|
||||
UIManager:close(dialog)
|
||||
end,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
UIManager:show(dialog)
|
||||
dialog:onShowKeyboard(true)
|
||||
end
|
||||
|
||||
function IRC:promptAddServer()
|
||||
local fields = {
|
||||
{ text = "", hint = _("Display name (optional)") },
|
||||
{ text = self.username or "koreader", hint = _("Nick (per-server)") },
|
||||
{ text = "irc.libera.chat", hint = _("Host") },
|
||||
{ text = "6667", hint = _("Port") },
|
||||
{ text = "#koreader", hint = _("Channels (comma-separated)") },
|
||||
{ text = "", hint = _("Auth user (optional, e.g., user or user/network)") },
|
||||
{ text = "", hint = _("Auth password (optional)"), text_type = "password" },
|
||||
}
|
||||
local dialog
|
||||
dialog = MultiInputDialog:new{
|
||||
title = _("Add server"),
|
||||
fields = fields,
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Add"),
|
||||
is_default = true,
|
||||
callback = function()
|
||||
local name, nick, host, port_str, chans, auth_user, auth_pass = unpack(dialog:getFields())
|
||||
local port = tonumber(port_str) or 6667
|
||||
local channels = {}
|
||||
if chans and #chans > 0 then
|
||||
for ch in chans:gmatch("[^,%s]+") do table.insert(channels, ch) end
|
||||
end
|
||||
if not host or host == "" then
|
||||
UIManager:show(InfoMessage:new{ text = _("Host is required."), timeout = 2 })
|
||||
return
|
||||
end
|
||||
table.insert(self.servers, {
|
||||
name = name,
|
||||
nick = (nick ~= "" and nick or nil),
|
||||
host = host,
|
||||
port = port,
|
||||
channels = channels,
|
||||
auth_user = (auth_user ~= "" and auth_user or nil),
|
||||
auth_pass = (auth_pass ~= "" and auth_pass or nil),
|
||||
})
|
||||
self:onFlushSettings()
|
||||
UIManager:close(dialog)
|
||||
UIManager:show(InfoMessage:new{ text = _("Server added."), timeout = 2 })
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Cancel"),
|
||||
callback = function() UIManager:close(dialog) end,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
UIManager:show(dialog)
|
||||
dialog:onShowKeyboard(true)
|
||||
end
|
||||
|
||||
function IRC:promptEditServer(server, index)
|
||||
local fields = {
|
||||
{ text = server.name or "", hint = _("Display name (optional)") },
|
||||
{ text = server.nick or self.username or "koreader", hint = _("Nick (per-server)") },
|
||||
{ text = server.host or "", hint = _("Host") },
|
||||
{ text = tostring(server.port or 6667), hint = _("Port") },
|
||||
{ text = table.concat(server.channels or {}, ", "), hint = _("Channels (comma-separated)") },
|
||||
{ text = server.auth_user or "", hint = _("Auth user (optional, e.g., user or user/network)") },
|
||||
{ text = server.auth_pass or "", hint = _("Auth password (optional)"), text_type = "password" },
|
||||
}
|
||||
local dialog
|
||||
dialog = MultiInputDialog:new{
|
||||
title = _("Edit server"),
|
||||
fields = fields,
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Save"),
|
||||
is_default = true,
|
||||
callback = function()
|
||||
local name, nick, host, port_str, chans, auth_user, auth_pass = unpack(dialog:getFields())
|
||||
local port = tonumber(port_str) or 6667
|
||||
local channels = {}
|
||||
if chans and #chans > 0 then
|
||||
for ch in chans:gmatch("[^,%s]+") do table.insert(channels, ch) end
|
||||
end
|
||||
if not host or host == "" then
|
||||
UIManager:show(InfoMessage:new{ text = _("Host is required."), timeout = 2 })
|
||||
return
|
||||
end
|
||||
server.name = name
|
||||
server.nick = (nick ~= "" and nick or nil)
|
||||
server.host = host
|
||||
server.port = port
|
||||
server.channels = channels
|
||||
server.auth_user = (auth_user ~= "" and auth_user or nil)
|
||||
server.auth_pass = (auth_pass ~= "" and auth_pass or nil)
|
||||
self.servers[index] = server
|
||||
self:onFlushSettings()
|
||||
UIManager:close(dialog)
|
||||
UIManager:show(InfoMessage:new{ text = _("Server saved."), timeout = 2 })
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Cancel"),
|
||||
callback = function() UIManager:close(dialog) end,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
UIManager:show(dialog)
|
||||
dialog:onShowKeyboard(true)
|
||||
end
|
||||
|
||||
function IRC:connectToServer(server)
|
||||
-- If multiple channels, ask to select one; otherwise connect to the only one or none
|
||||
local function open_chat(channel)
|
||||
local function do_open()
|
||||
local nick = server.nick or self.username or "koreader"
|
||||
local view = IrcChatView:new{
|
||||
_server = {
|
||||
name = server.name,
|
||||
host = server.host,
|
||||
port = server.port,
|
||||
auth_user = server.auth_user,
|
||||
auth_pass = server.auth_pass,
|
||||
},
|
||||
_channel = channel,
|
||||
_nick = nick,
|
||||
}
|
||||
UIManager:show(view)
|
||||
end
|
||||
-- Ensure wifi/network is up before proceeding
|
||||
if NetworkMgr:willRerunWhenConnected(function() open_chat(channel) end) then
|
||||
return
|
||||
end
|
||||
socketutil:set_timeout(socketutil.DEFAULT_BLOCK_TIMEOUT, socketutil.DEFAULT_TOTAL_TIMEOUT)
|
||||
do_open()
|
||||
end
|
||||
|
||||
if server.channels and #server.channels > 1 then
|
||||
-- Prompt to pick a channel
|
||||
local buttons = {}
|
||||
for _, ch in ipairs(server.channels) do
|
||||
table.insert(buttons, { { text = ch, callback = function() UIManager:close(self._chan_dlg); open_chat(ch) end } })
|
||||
end
|
||||
self._chan_dlg = require("ui/widget/buttondialog"):new{
|
||||
title = _("Select channel"),
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(self._chan_dlg)
|
||||
else
|
||||
open_chat(server.channels and server.channels[1] or nil)
|
||||
end
|
||||
end
|
||||
|
||||
function IRC:onOpenIRC()
|
||||
-- Open root submenu via info message hint
|
||||
UIManager:show(InfoMessage:new{ text = _("Use Tools → IRC client"), timeout = 3 })
|
||||
end
|
||||
|
||||
return IRC
|
||||
|
||||
Reference in New Issue
Block a user