From 68c26a697b4f0172d935dffd94a04348b7880e4b Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 19 May 2026 14:50:31 +0200 Subject: [PATCH] Added sys/notice timeout config --- nomadnet/NomadNetworkApp.py | 13 +++++++++ nomadnet/RRC.py | 39 ++++++++++++++++++++++++++ nomadnet/ui/textui/Channels.py | 50 ++++++++++++++++++++++++---------- nomadnet/ui/textui/Guide.py | 12 ++++++++ 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/nomadnet/NomadNetworkApp.py b/nomadnet/NomadNetworkApp.py index 9a9a500..4e14b50 100644 --- a/nomadnet/NomadNetworkApp.py +++ b/nomadnet/NomadNetworkApp.py @@ -154,6 +154,7 @@ class NomadNetworkApp: self.rrc_history_per_room_cap = 500 self.rrc_filter_loaded_history = True + self.rrc_ephemeral_notices = 600 if not os.path.isdir(self.storagepath): os.makedirs(self.storagepath) @@ -918,6 +919,11 @@ class NomadNetworkApp: try: value = self.config["rrc"].as_bool(option) except Exception: value = True self.rrc_filter_loaded_history = value + + if option == "ephemeral_notices": + try: value = self.config["rrc"].as_float(option) + except Exception: value = 0 + self.rrc_ephemeral_notices = value*60 if "node" in self.config: if not "enable_node" in self.config["node"]: @@ -1248,6 +1254,13 @@ history_per_room_cap = 500 # loaded. filter_loaded_history = yes +# You can choose whether notices and sys +# messages persist indefinitely, or are +# removed from the message history after +# the specified time in minutes. Set to 0 +# to disable and keep forever. +ephemeral_notices = 10 + [node] # Whether to enable node hosting diff --git a/nomadnet/RRC.py b/nomadnet/RRC.py index 6cc28ab..1335fe1 100644 --- a/nomadnet/RRC.py +++ b/nomadnet/RRC.py @@ -201,6 +201,9 @@ class RRCHub: STATUS_CONNECTED = 2 STATUS_FAILED = 3 + CLEAN_HISTORY_INTERVAL = 5 + SYS_NOTICE_TIMEOUT = 600 + def __init__(self, manager, hub_hash, dest_name=None, name=None): self.manager = manager self.hub_hash = hub_hash @@ -242,6 +245,8 @@ class RRCHub: self._reconnect_attempts = 0 self._reconnect_timer = None self._pending_pings = {} + self._last_history_clean = 0 + self.clean_last_removed = 0 self.available_rooms = {} self._silent_list_pending = 0 @@ -631,6 +636,11 @@ class RRCHub: except Exception: return True return v + def _ephemeral_notices_history(self): + try: v = getattr(self.manager.app, "rrc_ephemeral_notices", self.SYS_NOTICE_TIMEOUT) + except Exception: return self.SYS_NOTICE_TIMEOUT + return v + def _entry_for(self, msg): return { H_KIND: msg.kind, @@ -723,6 +733,32 @@ class RRCHub: with self._lock: self.messages[room] = msgs + def _clean_history(self): + now = time.time() + cleaned = False + remove_after = self._ephemeral_notices_history() + if now > self._last_history_clean + self.CLEAN_HISTORY_INTERVAL: + RNS.log(f"Cleaning loaded message history", RNS.LOG_DEBUG) + with self._lock: + try: + for r in self.messages: + old = set() + for m in self.messages[r]: + age = now-m.ts/1000.0 + should_filter = False + if m.kind == "system": should_filter = True + elif m.kind == "notice": should_filter = True + if should_filter and age > remove_after: old.add(m) + + for m in old: + self.messages[r].remove(m) + cleaned = True + + except Exception as e: RNS.trace_exception(e) + + self._last_history_clean = time.time() + if cleaned: self.clean_last_removed = time.time() + def _record_message(self, msg, local=False): cap = self._per_room_cap() with self._lock: @@ -736,6 +772,7 @@ class RRCHub: if msg.mention: self.mention_rooms.add(msg.room) self._append_history(msg.room, msg) + self._clean_history() self.manager._notify_messages(self, msg) def _record_system(self, room, text): @@ -749,6 +786,7 @@ class RRCHub: if cap is not None and len(buf) > cap: del buf[:len(buf)-cap] self._append_history(room, msg) + self._clean_history() self.manager._notify_messages(self, msg) def _record_notice(self, msg): @@ -772,6 +810,7 @@ class RRCHub: self.unread_rooms.add(target_room) if target_room: self._append_history(target_room, msg) + self._clean_history() self.manager._notify_messages(self, msg) def get_messages(self, room): diff --git a/nomadnet/ui/textui/Channels.py b/nomadnet/ui/textui/Channels.py index b763182..513c88f 100644 --- a/nomadnet/ui/textui/Channels.py +++ b/nomadnet/ui/textui/Channels.py @@ -29,13 +29,6 @@ _LINK_RE = re.compile( r"|(?P(? self.last_history_clean: + try: + hub_msgs = self.hub.get_messages(self.room) if (self.hub is not None and self.room is not None) else [] + self.last_history_clean = time.time() + c = self.messagelist.body_len() + old = set() + for i in range(0, c): + msg = None + w = self.messagelist.get_item(i) + if hasattr(w, "_original_widget"): o = w._original_widget + else: o = None + if hasattr(o, "msg"): msg = o.msg + elif hasattr(w, "msg"): msg = w.msg + if msg and not msg in hub_msgs: old.add(i) + + for i in reversed(list(old)): self.messagelist.delete_position(i) + + except Exception as e: + RNS.log("Error while cleaning room history", RNS.LOG_ERROR) + RNS.trace_exception(e) + body.append(wrapped) cap = getattr(self.app, "rrc_history_per_room_cap", 0) if cap and cap > 0: @@ -865,17 +879,23 @@ def _message_widget(app, hub, m, link_delegate=None): evt_icon = g["arrow_l"] if m.text.endswith(" left") else g["arrow_r"] spans, has_links = _body_markup(m.text or "", body_attr="irc_system", own_nick=own_nick) markup = [_ts_prefix(m.ts), ("irc_system", evt_icon+" ")] + spans - return _wrap_text(markup, link_delegate if has_links else None) + final_widget = _wrap_text(markup, link_delegate if has_links else None) + final_widget.msg = m + return final_widget if m.kind == "notice": spans, has_links = _body_markup(m.text or "", body_attr="irc_notice", own_nick=own_nick) markup = [_ts_prefix(m.ts), ("irc_notice", g["info"]+" ")] + spans - return _wrap_text(markup, link_delegate if has_links else None) + final_widget = _wrap_text(markup, link_delegate if has_links else None) + final_widget.msg = m + return final_widget if m.kind == "error": spans, has_links = _body_markup(m.text or "", body_attr="irc_error", own_nick=own_nick) markup = [_ts_prefix(m.ts), ("irc_error", g["warning"]+" ")] + spans - return _wrap_text(markup, link_delegate if has_links else None) + final_widget = _wrap_text(markup, link_delegate if has_links else None) + final_widget.msg = m + return final_widget own = False try: @@ -895,7 +915,9 @@ def _message_widget(app, hub, m, link_delegate=None): body = m.text or "" spans, has_links = _body_markup(body, body_attr="body_text", own_nick=None if own else own_nick) markup = [_ts_prefix(m.ts), (nick_attr, "<"+sender+">"), ("body_text", " ")] + spans - return _wrap_text(markup, link_delegate if has_links else None) + final_widget = _wrap_text(markup, link_delegate if has_links else None) + final_widget.msg = m + return final_widget def _wrap_text(markup, link_delegate): diff --git a/nomadnet/ui/textui/Guide.py b/nomadnet/ui/textui/Guide.py index 6c5a292..66def19 100644 --- a/nomadnet/ui/textui/Guide.py +++ b/nomadnet/ui/textui/Guide.py @@ -781,6 +781,18 @@ This section hold configuration directives related to the RRC client behaviour. Maximum number of messages retained per room in the in-memory scrollback buffer, and the number of messages restored from on-disk history at startup. The on-disk log itself is appended to indefinitely; this cap only controls how much backlog is visible. Set to 0 to keep every message in memory. < +>>> +`!filter_loaded_history = yes`! +>>>> +Whether to filter notices and system message when initially loading room history. +< + +>>> +`!ephemeral_notices = 10`! +>>>> +If enabled, notices and system messages will disappear from the history after a while. 0 disables, otherwise sets the timeout in minutes. +< + >> Text UI Section This section hold configuration directives related to the look and feel of the text-based user interface of the program. It is delimited by the `![textui]`! header in the configuration file. Available directives, along with their default values, are as follows: