From a3bf538afe7f1c414d028e177659199a767f050c Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 16 Oct 2023 01:46:50 +0200 Subject: [PATCH] Added propagation node list sorting and manual delivery sync initiation --- nomadnet/Directory.py | 6 ++++ nomadnet/ui/textui/Guide.py | 29 +++++++++++------- nomadnet/ui/textui/Network.py | 57 ++++++++++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/nomadnet/Directory.py b/nomadnet/Directory.py index dc1ed68..1b9e876 100644 --- a/nomadnet/Directory.py +++ b/nomadnet/Directory.py @@ -264,6 +264,12 @@ def trust_level(self, source_hash, announced_display_name=None): else: return DirectoryEntry.UNKNOWN + def pn_trust_level(self, source_hash): + recalled_identity = RNS.Identity.recall(source_hash) + if recalled_identity != None: + associated_node = RNS.Destination.hash_from_name_and_identity("nomadnetwork.node", recalled_identity) + return self.trust_level(associated_node) + def sort_rank(self, source_hash): if source_hash in self.directory_entries: return self.directory_entries[source_hash].sort_rank diff --git a/nomadnet/ui/textui/Guide.py b/nomadnet/ui/textui/Guide.py index 67aa856..1b64a4e 100644 --- a/nomadnet/ui/textui/Guide.py +++ b/nomadnet/ui/textui/Guide.py @@ -217,6 +217,15 @@ def focus_reader(self): - Ctrl-W Close conversation >>`!Network Window`! +>>>Browser + - Ctrl-D Back + - Ctrl-F Forward + - Ctrl-R Reload page + - Ctrl-U Open URL entry dialog + - Ctrl-S Save connected node + - Ctrl-G Toggle fullscreen browser window + - Ctrl-W Disconnect from node + >>>Announce Stream - Ctrl-L Switch to Known Nodes list - Ctrl-X Delete selected announce @@ -227,14 +236,10 @@ def focus_reader(self): - Ctrl-X Delete selected node entry - Ctrl-P Display peered LXMF Propagation Nodes ->>>Browser - - Ctrl-D Back - - Ctrl-F Forward - - Ctrl-R Reload page - - Ctrl-U Open URL entry dialog - - Ctrl-S Save connected node - - Ctrl-G Toggle fullscreen browser window - - Ctrl-W Disconnect from node +>>>Peered LXMF Propagation Nodes + - Ctrl-L Switch to Announce Stream or Known Nodes + - Ctrl-X Break peering with selected node entry + - Ctrl-R Request immediate delivery sync of unhandled LXMs ''' TOPIC_CONCEPTS = '''>Concepts and Terminology @@ -302,11 +307,13 @@ def focus_reader(self): >>Distributed Message Store -All nodes on the network automatically form a distributed message store that allows users to exchange messages, even when they are not available at the same time. +All nodes on the network will automatically participate in a distributed message store that allows users to exchange messages, even when they are not connected to the network at the same time. + +When Nomad Network is configured to host a node, by default it also configures itself as an LXMF Propagation Node, and automatically discovers and peers with other propagation nodes on the network. This process is completely automatic and requires no configuration from the node operator. -When Nomad Network is configured to host a node, it also configures itself as an LXMF Propagation Node, and automatically discovers and peers with other propagation nodes on the network. This process is completely automatic and requires no configuration from the node operator. +If there is already an abundance of Propagation Nodes on the network, or the operator simply wishes to host a pageserving-only node, Propagation Node hosting can be disabled in the configuration file. -To view LXMF Propagation nodes that are currently peered with your node, go to the `![ Network ]`! part of the program and press `!Ctrl-P`!. +To view LXMF Propagation nodes that are currently peered with your node, go to the `![ Network ]`! part of the program and press `!Ctrl-P`!. In the list of peered Propagation Nodes, it is possible to break peering with a node by pressing `!Ctrl-X`!. It is also possible to request an immediate delivery sync of all unhandled messages for a node, by pressing `!Ctrl-R`!. The distributed message store is resilient to intermittency, and will remain functional as long as at least one node remains on the network. Nodes that were offline for a time will automatically be synced up to date when they regain connectivity. diff --git a/nomadnet/ui/textui/Network.py b/nomadnet/ui/textui/Network.py index 56b7d3b..19ef8af 100644 --- a/nomadnet/ui/textui/Network.py +++ b/nomadnet/ui/textui/Network.py @@ -2,6 +2,7 @@ import urwid import nomadnet import time +import threading from datetime import datetime from nomadnet.Directory import DirectoryEntry from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox, MODIFIER_KEY @@ -1626,6 +1627,8 @@ def keypress(self, size, key): nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") elif key == "ctrl x": self.delete_selected_entry() + elif key == "ctrl r": + self.sync_selected_entry() return super(LXMFPeers, self).keypress(size, key) @@ -1641,6 +1644,43 @@ def delete_selected_entry(self): self.delegate.reinit_lxmf_peers() self.delegate.show_peers() + def sync_selected_entry(self): + sync_grace = 10 + si = self.ilb.get_selected_item() + if si != None: + destination_hash = si.original_widget.destination_hash + if destination_hash in self.app.message_router.peers: + peer = self.app.message_router.peers[destination_hash] + if time.time() > peer.last_sync_attempt+sync_grace: + peer.next_sync_attempt = time.time()-1 + + def job(): + peer.sync() + threading.Thread(target=job, daemon=True).start() + + time.sleep(0.25) + + def dismiss_dialog(sender): + self.close_list_dialogs() + + dialog = ListDialogLineBox( + urwid.Pile([ + urwid.Text("A delivery sync of all unhandled LXMs was manually requested for the selected node\n", align="center"), + urwid.Columns([("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("OK", on_press=dismiss_dialog))]) + ]), title="!" + ) + dialog.delegate = self.delegate + bottom = self + + overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) + + options = self.delegate.left_pile.options("weight", 1) + self.delegate.left_pile.contents[0] = (overlay, options) + + + def close_list_dialogs(self): + self.delegate.reinit_lxmf_peers() + self.delegate.show_peers() def rebuild_widget_list(self): self.peer_list = self.app.message_router.peers @@ -1654,17 +1694,18 @@ def rebuild_widget_list(self): def make_peer_widgets(self): widget_list = [] - sorted_peers = sorted(self.peer_list, key=lambda pid: self.peer_list[pid].link_establishment_rate, reverse=True) + sorted_peers = sorted(self.peer_list, key=lambda pid: (self.app.directory.pn_trust_level(pid), self.peer_list[pid].link_establishment_rate), reverse=True) for peer_id in sorted_peers: peer = self.peer_list[peer_id] - pe = LXMFPeerEntry(self.app, peer, self) + trust_level = self.app.directory.pn_trust_level(peer_id) + pe = LXMFPeerEntry(self.app, peer, self, trust_level) pe.destination_hash = peer.destination_hash widget_list.append(pe) return widget_list class LXMFPeerEntry(urwid.WidgetWrap): - def __init__(self, app, peer, delegate): + def __init__(self, app, peer, delegate, trust_level): destination_hash = peer.destination_hash self.app = app @@ -1686,16 +1727,18 @@ def __init__(self, app, peer, delegate): if hasattr(peer, "alive"): if peer.alive: alive_string = "Available" - style = "list_normal" - focus_style = "list_focus" + if trust_level == DirectoryEntry.TRUSTED: + style = "list_trusted" + focus_style = "list_focus_trusted" + else: + style = "list_normal" + focus_style = "list_focus" else: alive_string = "Unresponsive" style = "list_unresponsive" focus_style = "list_focus_unresponsive" widget = ListEntry(sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))+"\n "+str(len(peer.unhandled_messages))+" unhandled LXMs, "+RNS.prettysize(peer.link_establishment_rate/8, "b")+"/s LER") - # urwid.connect_signal(widget, "click", delegate.connect_node, node) - self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget.destination_hash = destination_hash urwid.WidgetWrap.__init__(self, self.display_widget)