diff --git a/main.lua b/main.lua index 5712730..2f954cb 100644 --- a/main.lua +++ b/main.lua @@ -17,6 +17,7 @@ local socket = require("socket") local socketutil = require("socketutil") local _ = require("gettext") local T = require("ffi/util").template +local lfs = require("libs/libkoreader-lfs") -- Chat view based on TextViewer for scrollable text & menu buttons local IrcChatView = TextViewer:extend{ @@ -38,6 +39,10 @@ local IrcChatView = TextViewer:extend{ _current_target = nil, -- active target ("*" for server console) _server_label = nil, _unread = nil, -- map target => count of unseen messages + -- History persistence + _history_dir = nil, + _history_server_dir = nil, + _history_preload_lines = 500, } function IrcChatView:init() @@ -64,6 +69,8 @@ function IrcChatView:init() 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 = {} + -- Init history before selecting initial buffer + self:initHistory() -- default to server console target self:switchTarget("*") self:updateTitle() @@ -72,6 +79,75 @@ function IrcChatView:init() UIManager:nextTick(function() self:startConnection() end) end +-- History helpers +function IrcChatView:sanitizePathPart(part) + part = part or "" + part = part:gsub("[^%w%._%-#@]+", "_") + if part == "" then part = "_" end + return part +end + +function IrcChatView:getServerId() + local name = self._server and (self._server.name or "") or "" + local host = self._server and (self._server.host or "") or "" + local port = self._server and tostring(self._server.port or 6667) or "6667" + local id = string.format("%s_%s_%s", name, host, port) + return self:sanitizePathPart(id) +end + +function IrcChatView:initHistory() + self._history_dir = DataStorage:getDataDir() .. "/irc_logs" + lfs.mkdir(self._history_dir) + self._history_server_dir = self._history_dir .. "/" .. self:getServerId() + lfs.mkdir(self._history_server_dir) +end + +function IrcChatView:getLogPath(target) + target = target or "*" + local fname = target == "*" and "console" or self:sanitizePathPart(target) + return string.format("%s/%s.log", self._history_server_dir, fname) +end + +function IrcChatView:writeHistory(target, line) + local path = self:getLogPath(target) + local f = io.open(path, "a") + if f then + f:write(line, "\n") + f:close() + end +end + +function IrcChatView:readLastLines(path, max_lines) + local f = io.open(path, "r") + if not f then return nil end + local buf = {} + local count = 0 + for l in f:lines() do + table.insert(buf, l) + count = count + 1 + if count > max_lines then + table.remove(buf, 1) + count = count - 1 + end + end + f:close() + if #buf == 0 then return nil end + return table.concat(buf, "\n") +end + +function IrcChatView:preloadHistory(target) + target = target or self._current_target or "*" + if self._buffers[target] and #self._buffers[target] > 0 then return end + local path = self:getLogPath(target) + local content = self:readLastLines(path, self._history_preload_lines) + if content and #content > 0 then + self._buffers[target] = content + if target == (self._current_target or "*") and self.scroll_text_w and self.scroll_text_w.text_widget then + self.scroll_text_w.text_widget:setText(content) + end + end +end + function IrcChatView:updateTitle() local tgt = self._current_target local new_title @@ -106,6 +182,8 @@ end function IrcChatView:switchTarget(target) if not target then return end self:ensureBuffer(target) + -- Preload history (if any) when switching to a buffer + self:preloadHistory(target) self._current_target = target self:updateTitle() if self.scroll_text_w and self.scroll_text_w.text_widget then @@ -124,6 +202,8 @@ function IrcChatView:appendLine(line, target) local buf = self._buffers[target] buf = (buf and #buf > 0) and (buf .. "\n" .. prefix .. line) or (prefix .. line) self._buffers[target] = buf + -- Persist to history + self:writeHistory(target, prefix .. line) 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)