mirror of
https://github.com/markqvist/NomadNet.git
synced 2025-04-23 08:29:22 -04:00
Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aa4feeb29c | ||
![]() |
ad985a2552 | ||
![]() |
42fdc30cf8 | ||
![]() |
e79689010e | ||
![]() |
e4f57d6260 | ||
![]() |
03d1b22b8f | ||
![]() |
f8775adbab | ||
![]() |
c0dac0eadb | ||
![]() |
c198d516da | ||
![]() |
d9886980fa | ||
![]() |
7ced4c3659 | ||
![]() |
c3c8f99131 | ||
![]() |
9f00ffbae6 | ||
![]() |
a26edd21db | ||
![]() |
46f3a4127c | ||
![]() |
eeb15dcb43 | ||
![]() |
9ef34fc774 | ||
![]() |
704a4ea828 | ||
![]() |
5f31aeb3c1 | ||
![]() |
f000073594 | ||
![]() |
e332be6211 | ||
![]() |
9ee052a122 | ||
![]() |
11fda51017 | ||
![]() |
d5cf34f9d9 | ||
![]() |
d13c8af88e | ||
![]() |
7cd025e236 | ||
![]() |
c48a8d2a09 | ||
![]() |
4fb97eecc5 | ||
![]() |
74e7e4e806 | ||
![]() |
01a5c21016 | ||
![]() |
5e8a14d7a6 | ||
![]() |
6edf531fbf | ||
![]() |
eb3ff558c0 | ||
![]() |
b5bf730e07 | ||
![]() |
c52c49371b | ||
![]() |
46eee79da2 | ||
![]() |
bd909c9f58 | ||
![]() |
c95fd83400 | ||
![]() |
7d18a103cf | ||
![]() |
00855c32a7 | ||
![]() |
d8cfc69ac6 | ||
![]() |
ccc41a5789 | ||
![]() |
7b38d4f80e |
2
.github/ISSUE_TEMPLATE/🐛-bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/🐛-bug-report.md
vendored
@ -12,7 +12,7 @@ Before creating a bug report on this issue tracker, you **must** read the [Contr
|
||||
|
||||
- The issue tracker is used by developers of this project. **Do not use it to ask general questions, or for support requests**.
|
||||
- Ideas and feature requests can be made on the [Discussions](https://github.com/markqvist/Reticulum/discussions). **Only** feature requests accepted by maintainers and developers are tracked and included on the issue tracker. **Do not post feature requests here**.
|
||||
- After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), delete this section from your bug report.
|
||||
- After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), **delete this section only** (*"Read the Contribution Guidelines"*) from your bug report, **and fill in all the other sections**.
|
||||
|
||||
**Describe the Bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
@ -16,10 +16,6 @@ ENV PATH="/home/myuser/.local/bin:${PATH}"
|
||||
|
||||
################### BEGIN NomadNet ###########################################
|
||||
|
||||
COPY --chown=myuser:myuser requirements.txt requirements.txt
|
||||
|
||||
RUN pip install --user -r requirements.txt
|
||||
|
||||
|
||||
COPY --chown=myuser:myuser . .
|
||||
|
||||
|
@ -3,6 +3,7 @@ import RNS
|
||||
import LXMF
|
||||
import time
|
||||
import nomadnet
|
||||
import threading
|
||||
import RNS.vendor.umsgpack as msgpack
|
||||
|
||||
class PNAnnounceHandler:
|
||||
@ -29,7 +30,7 @@ class PNAnnounceHandler:
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||
|
||||
class Directory:
|
||||
ANNOUNCE_STREAM_MAXLENGTH = 64
|
||||
ANNOUNCE_STREAM_MAXLENGTH = 256
|
||||
|
||||
aspect_filter = "nomadnetwork.node"
|
||||
@staticmethod
|
||||
@ -55,6 +56,7 @@ class Directory:
|
||||
self.directory_entries = {}
|
||||
self.announce_stream = []
|
||||
self.app = app
|
||||
self.announce_lock = threading.Lock()
|
||||
self.load_from_disk()
|
||||
|
||||
self.pn_announce_handler = PNAnnounceHandler(self)
|
||||
@ -124,90 +126,94 @@ class Directory:
|
||||
RNS.log("Could not load directory from disk. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def lxmf_announce_received(self, source_hash, app_data):
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
with self.announce_lock:
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "peer"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "peer"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
|
||||
if hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
if hasattr(self.app, "ui") and self.app.ui != None:
|
||||
if hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
|
||||
def node_announce_received(self, source_hash, app_data, associated_peer):
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
with self.announce_lock:
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "node"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
|
||||
if self.trust_level(associated_peer) == DirectoryEntry.TRUSTED:
|
||||
existing_entry = self.find(source_hash)
|
||||
if not existing_entry:
|
||||
node_entry = DirectoryEntry(source_hash, display_name=app_data.decode("utf-8"), trust_level=DirectoryEntry.TRUSTED, hosts_node=True)
|
||||
self.remember(node_entry)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "node"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
|
||||
if self.trust_level(associated_peer) == DirectoryEntry.TRUSTED:
|
||||
existing_entry = self.find(source_hash)
|
||||
if not existing_entry:
|
||||
node_entry = DirectoryEntry(source_hash, display_name=app_data.decode("utf-8"), trust_level=DirectoryEntry.TRUSTED, hosts_node=True)
|
||||
self.remember(node_entry)
|
||||
|
||||
if hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
if hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
|
||||
def pn_announce_received(self, source_hash, app_data, associated_peer, associated_node):
|
||||
found_node = None
|
||||
for sh in self.directory_entries:
|
||||
if sh == associated_node:
|
||||
found_node = True
|
||||
break
|
||||
with self.announce_lock:
|
||||
found_node = None
|
||||
for sh in self.directory_entries:
|
||||
if sh == associated_node:
|
||||
found_node = True
|
||||
break
|
||||
|
||||
for e in self.announce_stream:
|
||||
if e[1] == associated_node:
|
||||
found_node = True
|
||||
break
|
||||
for e in self.announce_stream:
|
||||
if e[1] == associated_node:
|
||||
found_node = True
|
||||
break
|
||||
|
||||
if not found_node:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
if not found_node:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "pn"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "pn"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
self.announce_stream.pop()
|
||||
|
||||
if hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
if hasattr(self.app, "ui") and hasattr(self.app.ui, "main_display"):
|
||||
self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
|
||||
def remove_announce_with_timestamp(self, timestamp):
|
||||
selected_announce = None
|
||||
@ -339,7 +345,7 @@ class Directory:
|
||||
if e.hosts_node:
|
||||
node_list.append(e)
|
||||
|
||||
node_list.sort(key = lambda e: (e.sort_rank if e.sort_rank != None else 2^32, DirectoryEntry.TRUSTED-e.trust_level, e.display_name))
|
||||
node_list.sort(key = lambda e: (e.sort_rank if e.sort_rank != None else 2^32, DirectoryEntry.TRUSTED-e.trust_level, e.display_name if e.display_name != None else "_"))
|
||||
return node_list
|
||||
|
||||
def number_of_known_nodes(self):
|
||||
|
@ -1,4 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import RNS
|
||||
import time
|
||||
import threading
|
||||
@ -159,7 +161,7 @@ class Node:
|
||||
try:
|
||||
if request_allowed:
|
||||
RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE)
|
||||
if os.access(file_path, os.X_OK):
|
||||
if not RNS.vendor.platformutils.is_windows() and os.access(file_path, os.X_OK):
|
||||
env_map = {}
|
||||
if "PATH" in os.environ:
|
||||
env_map["PATH"] = os.environ["PATH"]
|
||||
|
@ -19,7 +19,7 @@ from datetime import datetime
|
||||
import RNS.vendor.umsgpack as msgpack
|
||||
|
||||
from ._version import __version__
|
||||
from .vendor.configobj import ConfigObj
|
||||
from RNS.vendor.configobj import ConfigObj
|
||||
|
||||
class NomadNetworkApp:
|
||||
time_format = "%Y-%m-%d %H:%M:%S"
|
||||
@ -290,9 +290,20 @@ class NomadNetworkApp:
|
||||
|
||||
self.directory = nomadnet.Directory(self)
|
||||
|
||||
static_peers = []
|
||||
for static_peer in self.static_peers:
|
||||
try:
|
||||
dh = bytes.fromhex(static_peer)
|
||||
if len(dh) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
|
||||
raise ValueError("Invalid destination length")
|
||||
static_peers.append(dh)
|
||||
except Exception as e:
|
||||
RNS.log(f"Could not decode static peer destination hash {static_peer}: {e}", RNS.LOG_ERROR)
|
||||
|
||||
self.message_router = LXMF.LXMRouter(
|
||||
identity = self.identity, storagepath = self.storagepath, autopeer = True,
|
||||
propagation_limit = self.lxmf_max_propagation_size, delivery_limit = self.lxmf_max_incoming_size,
|
||||
max_peers = self.max_peers, static_peers = static_peers,
|
||||
)
|
||||
|
||||
self.message_router.register_delivery_callback(self.lxmf_delivery)
|
||||
@ -526,7 +537,7 @@ class NomadNetworkApp:
|
||||
RNS.log("Could not autoselect a propagation node! LXMF propagation will not be available until a trusted node announces on the network, or a propagation node is manually selected.", RNS.LOG_WARNING)
|
||||
else:
|
||||
pn_name_str = ""
|
||||
RNS.log("Selecting "+RNS.prettyhexrep(selected_node)+pn_name_str+" as default LXMF propagation node", RNS.LOG_INFO)
|
||||
RNS.log("Selecting "+RNS.prettyhexrep(selected_node)+pn_name_str+" as default LXMF propagation node", RNS.LOG_DEBUG)
|
||||
self.message_router.set_outbound_propagation_node(selected_node)
|
||||
|
||||
def get_user_selected_propagation_node(self):
|
||||
@ -920,6 +931,19 @@ class NomadNetworkApp:
|
||||
else:
|
||||
self.prioritised_lxmf_destinations = []
|
||||
|
||||
if "static_peers" in self.config["node"]:
|
||||
self.static_peers = self.config["node"].as_list("static_peers")
|
||||
else:
|
||||
self.static_peers = []
|
||||
|
||||
if not "max_peers" in self.config["node"]:
|
||||
self.max_peers = None
|
||||
else:
|
||||
value = self.config["node"].as_int("max_peers")
|
||||
if value < 0:
|
||||
value = 0
|
||||
self.max_peers = value
|
||||
|
||||
if not "message_storage_limit" in self.config["node"]:
|
||||
self.message_storage_limit = 2000
|
||||
else:
|
||||
@ -1136,13 +1160,15 @@ announce_interval = 360
|
||||
# Whether to announce when the node starts.
|
||||
announce_at_start = Yes
|
||||
|
||||
# By default, when Nomad Network is hosting a
|
||||
# node, it will also act as an LXMF propagation
|
||||
# When Nomad Network is hosting a page-serving
|
||||
# node, it can also act as an LXMF propagation
|
||||
# node. If there is already a large amount of
|
||||
# propagation nodes on the network, or you
|
||||
# simply want to run a pageserving-only node,
|
||||
# you can disable running a propagation node.
|
||||
# disable_propagation = False
|
||||
# Due to lots of propagation nodes being
|
||||
# available, this is currently the default.
|
||||
disable_propagation = Yes
|
||||
|
||||
# The maximum amount of storage to use for
|
||||
# the LXMF Propagation Node message store,
|
||||
@ -1176,9 +1202,16 @@ max_transfer_size = 256
|
||||
# and generally you do not need to use it.
|
||||
# prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf
|
||||
|
||||
# Automatic rescan interval of the pages directory in minutes.
|
||||
# Default: int = 0 (no rescan)
|
||||
page_refresh_interval = 0
|
||||
# You can configure the maximum number of other
|
||||
# propagation nodes that this node will peer
|
||||
# with automatically. The default is 50.
|
||||
# max_peers = 25
|
||||
|
||||
# You can configure a list of static propagation
|
||||
# node peers, that this node will always be
|
||||
# peered with, by specifying a list of
|
||||
# destination hashes.
|
||||
# static_peers = e17f833c4ddf8890dd3a79a6fea8161d, 5a2d0029b6e5ec87020abaea0d746da4
|
||||
|
||||
# You can specify the interval in minutes for
|
||||
# rescanning the hosted pages path. By default,
|
||||
|
@ -1 +1 @@
|
||||
__version__ = "0.5.5"
|
||||
__version__ = "0.6.2"
|
||||
|
@ -666,9 +666,9 @@ Determines the interval in minutes for rescanning the hosted files path. By defa
|
||||
<
|
||||
|
||||
>>>
|
||||
`!disable_propagation = no`!
|
||||
`!disable_propagation = yes`!
|
||||
>>>>
|
||||
By default, when Nomad Network is hosting a node, it will also run an LXMF propagation node. If there is already a large amount of propagation nodes on the network, or you simply want to run a pageserving-only node, you can disable running a propagation node.
|
||||
When Nomad Network is hosting a node, it can also run an LXMF propagation node. If there is already a large amount of propagation nodes on the network, or you simply want to run a pageserving-only node, you can disable running a propagation node.
|
||||
<
|
||||
|
||||
>>>
|
||||
@ -689,6 +689,18 @@ The maximum accepted transfer size per incoming propagation transfer, in kilobyt
|
||||
Configures the LXMF Propagation Node to prioritise storing messages for certain destinations. If the message store reaches the specified limit, LXMF will prioritise keeping messages for destinations specified with this option. This setting is optional, and generally you do not need to use it.
|
||||
<
|
||||
|
||||
>>>
|
||||
`!max_peers = 25`!
|
||||
>>>>
|
||||
Configures the maximum number of other nodes the LXMF Propagation Node will automatically peer with. The default is 50, but can be lowered or increased according to available resources.
|
||||
<
|
||||
|
||||
>>>
|
||||
`!static_peers = e17f833c4ddf8890dd3a79a6fea8161d, 5a2d0029b6e5ec87020abaea0d746da4`!
|
||||
>>>>
|
||||
Configures the LXMF Propagation Node to always maintain propagation node peering with the specified list of destination hashes.
|
||||
<
|
||||
|
||||
>> Printing Section
|
||||
|
||||
This section holds configuration directives related to printing. It is delimited by the `![printing]`! header in the configuration file. Available directives, along with example values, are as follows:
|
||||
|
@ -168,7 +168,7 @@ def parse_line(line, state, url_delegate):
|
||||
fn = o["name"]
|
||||
fs = o["style"]
|
||||
fmask = "*" if o["masked"] else None
|
||||
f = urwid.Edit(caption="", edit_text=fd, align=state["align"], multiline=False, mask=fmask)
|
||||
f = urwid.Edit(caption="", edit_text=fd, align=state["align"], multiline=True, mask=fmask)
|
||||
f.field_name = fn
|
||||
fa = urwid.AttrMap(f, fs)
|
||||
widgets.append((fw, fa))
|
||||
|
@ -141,7 +141,7 @@ class AnnounceInfo(urwid.WidgetWrap):
|
||||
if is_node:
|
||||
try:
|
||||
existing_conversations = nomadnet.Conversation.conversation_list(self.app)
|
||||
|
||||
|
||||
source_hash_text = RNS.hexrep(op_hash, delimit=False)
|
||||
display_name = op_str
|
||||
|
||||
@ -162,7 +162,7 @@ class AnnounceInfo(urwid.WidgetWrap):
|
||||
show_announce_stream(None)
|
||||
try:
|
||||
existing_conversations = nomadnet.Conversation.conversation_list(self.app)
|
||||
|
||||
|
||||
source_hash_text = RNS.hexrep(source_hash, delimit=False)
|
||||
display_name = data_str
|
||||
|
||||
@ -316,7 +316,7 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
|
||||
info_widget = AnnounceInfo(announce, parent, self.app)
|
||||
options = parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
|
||||
parent.left_pile.contents[0] = (info_widget, options)
|
||||
|
||||
|
||||
except KeyError as e:
|
||||
def dismiss_dialog(sender):
|
||||
self.delegate.parent.close_list_dialogs()
|
||||
@ -368,6 +368,10 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
|
||||
def timestamp(self):
|
||||
return self.timestamp
|
||||
|
||||
class TabButton(urwid.Button):
|
||||
button_left = urwid.Text("[")
|
||||
button_right = urwid.Text("]")
|
||||
|
||||
class AnnounceStream(urwid.WidgetWrap):
|
||||
def __init__(self, app, parent):
|
||||
self.app = app
|
||||
@ -376,11 +380,24 @@ class AnnounceStream(urwid.WidgetWrap):
|
||||
self.timeout = self.app.config["textui"]["animation_interval"]*2
|
||||
self.ilb = None
|
||||
self.no_content = True
|
||||
|
||||
self.current_tab = "nodes"
|
||||
|
||||
self.added_entries = []
|
||||
self.widget_list = []
|
||||
self.update_widget_list()
|
||||
|
||||
# Create tab buttons
|
||||
self.tab_nodes = TabButton("Nodes", on_press=self.show_nodes_tab)
|
||||
self.tab_peers = TabButton("Peers", on_press=self.show_peers_tab)
|
||||
self.tab_pn = TabButton("Propagation Nodes", on_press=self.show_pn_tab)
|
||||
|
||||
# Create tab bar with proportional widths
|
||||
self.tab_bar = urwid.Columns([
|
||||
('weight', 1, self.tab_nodes),
|
||||
('weight', 1, self.tab_peers),
|
||||
('weight', 3, self.tab_pn),
|
||||
], dividechars=1) # Add 1 character spacing between tabs
|
||||
|
||||
self.ilb = ExceptionHandlingListBox(
|
||||
self.widget_list,
|
||||
on_selection_change=self.list_selection,
|
||||
@ -389,7 +406,13 @@ class AnnounceStream(urwid.WidgetWrap):
|
||||
#highlight_offFocus="list_off_focus"
|
||||
)
|
||||
|
||||
self.display_widget = self.ilb
|
||||
# Combine tab bar and list box
|
||||
self.pile = urwid.Pile([
|
||||
('pack', self.tab_bar),
|
||||
('weight', 1, self.ilb),
|
||||
])
|
||||
|
||||
self.display_widget = self.pile
|
||||
super().__init__(urwid.LineBox(self.display_widget, title="Announce Stream"))
|
||||
|
||||
def keypress(self, size, key):
|
||||
@ -397,7 +420,7 @@ class AnnounceStream(urwid.WidgetWrap):
|
||||
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
|
||||
elif key == "ctrl x":
|
||||
self.delete_selected_entry()
|
||||
|
||||
|
||||
return super(AnnounceStream, self).keypress(size, key)
|
||||
|
||||
def delete_selected_entry(self):
|
||||
@ -412,28 +435,45 @@ class AnnounceStream(urwid.WidgetWrap):
|
||||
self.update_widget_list()
|
||||
|
||||
def update_widget_list(self):
|
||||
self.widget_list = []
|
||||
new_entries = []
|
||||
|
||||
for e in self.app.directory.announce_stream:
|
||||
if not e[0] in self.added_entries:
|
||||
self.added_entries.insert(0, e[0])
|
||||
new_entries.insert(0, e)
|
||||
announce_type = e[3]
|
||||
|
||||
# Filter based on current tab
|
||||
if self.current_tab == "nodes" and (announce_type == "node" or announce_type == True):
|
||||
new_entries.append(e)
|
||||
elif self.current_tab == "peers" and (announce_type == "peer" or announce_type == False):
|
||||
new_entries.append(e)
|
||||
elif self.current_tab == "pn" and announce_type == "pn":
|
||||
new_entries.append(e)
|
||||
|
||||
for e in new_entries:
|
||||
nw = AnnounceStreamEntry(self.app, e, self)
|
||||
nw.timestamp = e[0]
|
||||
self.widget_list.insert(0, nw)
|
||||
self.widget_list.append(nw)
|
||||
|
||||
if len(new_entries) > 0:
|
||||
self.no_content = False
|
||||
if self.ilb != None:
|
||||
self.ilb.set_body(self.widget_list)
|
||||
else:
|
||||
if len(self.widget_list) == 0:
|
||||
self.no_content = True
|
||||
|
||||
if self.ilb != None:
|
||||
self.ilb.set_body(self.widget_list)
|
||||
self.no_content = True
|
||||
self.widget_list = [urwid.Text(f"No {self.current_tab} announces", align='center')]
|
||||
|
||||
if self.ilb:
|
||||
self.ilb.set_body(self.widget_list)
|
||||
|
||||
def show_nodes_tab(self, button):
|
||||
self.current_tab = "nodes"
|
||||
self.update_widget_list()
|
||||
|
||||
def show_peers_tab(self, button):
|
||||
self.current_tab = "peers"
|
||||
self.update_widget_list()
|
||||
|
||||
def show_pn_tab(self, button):
|
||||
self.current_tab = "pn"
|
||||
self.update_widget_list()
|
||||
|
||||
def list_selection(self, arg1, arg2):
|
||||
pass
|
||||
@ -596,7 +636,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
if node_ident != None:
|
||||
try:
|
||||
existing_conversations = nomadnet.Conversation.conversation_list(self.app)
|
||||
|
||||
|
||||
source_hash_text = RNS.hexrep(op_hash, delimit=False)
|
||||
display_name = op_str
|
||||
|
||||
@ -632,7 +672,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
trust_level = DirectoryEntry.UNTRUSTED
|
||||
if r_unknown.get_state() == True:
|
||||
trust_level = DirectoryEntry.UNKNOWN
|
||||
|
||||
|
||||
if r_trusted.get_state() == True:
|
||||
trust_level = DirectoryEntry.TRUSTED
|
||||
|
||||
@ -699,7 +739,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
pile_widgets.insert(4, operator_entry)
|
||||
|
||||
pile = urwid.Pile(pile_widgets)
|
||||
|
||||
|
||||
pile.focus_position = len(pile.contents)-1
|
||||
button_columns.focus_position = 0
|
||||
|
||||
@ -733,7 +773,7 @@ class KnownNodes(urwid.WidgetWrap):
|
||||
g = self.app.ui.glyphs
|
||||
|
||||
self.widget_list = self.make_node_widgets()
|
||||
|
||||
|
||||
self.ilb = ExceptionHandlingListBox(
|
||||
self.widget_list,
|
||||
on_selection_change=self.node_list_selection,
|
||||
@ -767,7 +807,7 @@ class KnownNodes(urwid.WidgetWrap):
|
||||
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
|
||||
elif key == "ctrl x":
|
||||
self.delete_selected_entry()
|
||||
|
||||
|
||||
return super(KnownNodes, self).keypress(size, key)
|
||||
|
||||
|
||||
@ -910,7 +950,7 @@ class NodeEntry(urwid.WidgetWrap):
|
||||
focus_style = "list_focus_untrusted"
|
||||
|
||||
type_symbol = g["node"]
|
||||
|
||||
|
||||
widget = ListEntry(type_symbol+" "+display_str)
|
||||
urwid.connect_signal(widget, "click", delegate.connect_node, node)
|
||||
|
||||
@ -1209,7 +1249,7 @@ class LocalPeer(urwid.WidgetWrap):
|
||||
|
||||
#overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=4, right=4)
|
||||
overlay = dialog
|
||||
|
||||
|
||||
self.dialog_open = True
|
||||
options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
|
||||
self.parent.left_pile.contents[1] = (overlay, options)
|
||||
@ -1226,7 +1266,7 @@ class LocalPeer(urwid.WidgetWrap):
|
||||
self.t_last_announce.update_time()
|
||||
|
||||
announce_button = urwid.Button("Announce Now", on_press=announce_query)
|
||||
|
||||
|
||||
self.display_widget = urwid.Pile(
|
||||
[
|
||||
t_id,
|
||||
@ -1270,13 +1310,13 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
def show_peer_info(sender):
|
||||
options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
|
||||
self.parent.left_pile.contents[1] = (LocalPeer(self.app, self.parent), options)
|
||||
|
||||
|
||||
if self.app.enable_node:
|
||||
if self.app.node != None:
|
||||
display_name = self.app.node.name
|
||||
else:
|
||||
display_name = None
|
||||
|
||||
|
||||
if display_name == None:
|
||||
display_name = ""
|
||||
|
||||
@ -1308,7 +1348,7 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
|
||||
#overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=4, right=4)
|
||||
overlay = dialog
|
||||
|
||||
|
||||
self.dialog_open = True
|
||||
options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
|
||||
self.parent.left_pile.contents[1] = (overlay, options)
|
||||
@ -1594,14 +1634,14 @@ class NetworkDisplay():
|
||||
if self.list_display == 1:
|
||||
parent = self.app.ui.main_display.sub_displays.network_display
|
||||
selected_node_entry = parent.known_nodes_display.ilb.get_selected_item()
|
||||
if selected_node_entry != None:
|
||||
selected_node_hash = selected_node_entry._get_base_widget().display_widget.source_hash
|
||||
|
||||
if selected_node_hash != None:
|
||||
if selected_node_entry is not None:
|
||||
selected_node_hash = selected_node_entry.base_widget.display_widget.source_hash
|
||||
|
||||
if selected_node_hash is not None:
|
||||
info_widget = KnownNodeInfo(selected_node_hash)
|
||||
options = parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
|
||||
parent.left_pile.contents[0] = (info_widget, options)
|
||||
|
||||
|
||||
def focus_lists(self):
|
||||
self.columns.focus_position = 0
|
||||
|
||||
@ -1612,9 +1652,15 @@ class NetworkDisplay():
|
||||
self.announce_stream_display.rebuild_widget_list()
|
||||
|
||||
def reinit_lxmf_peers(self):
|
||||
if self.lxmf_peers_display:
|
||||
si = self.lxmf_peers_display.ilb.get_selected_position()
|
||||
else:
|
||||
si = None
|
||||
self.lxmf_peers_display = LXMFPeers(self.app)
|
||||
self.lxmf_peers_display.delegate = self
|
||||
self.close_list_dialogs()
|
||||
if si != None:
|
||||
self.lxmf_peers_display.ilb.select_item(si)
|
||||
|
||||
def close_list_dialogs(self):
|
||||
if self.list_display == 0:
|
||||
@ -1683,7 +1729,7 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
self.delete_selected_entry()
|
||||
elif key == "ctrl r":
|
||||
self.sync_selected_entry()
|
||||
|
||||
|
||||
return super(LXMFPeers, self).keypress(size, key)
|
||||
|
||||
|
||||
@ -1707,7 +1753,7 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
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()
|
||||
@ -1753,7 +1799,7 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
|
||||
def make_peer_widgets(self):
|
||||
widget_list = []
|
||||
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)
|
||||
sorted_peers = sorted(self.peer_list, key=lambda pid: (self.app.directory.pn_trust_level(pid), self.peer_list[pid].sync_transfer_rate), reverse=True)
|
||||
for peer_id in sorted_peers:
|
||||
peer = self.peer_list[peer_id]
|
||||
trust_level = self.app.directory.pn_trust_level(peer_id)
|
||||
@ -1772,7 +1818,7 @@ class LXMFPeerEntry(urwid.WidgetWrap):
|
||||
|
||||
node_identity = RNS.Identity.recall(destination_hash)
|
||||
display_str = RNS.prettyhexrep(destination_hash)
|
||||
if node_identity != None:
|
||||
if node_identity != None:
|
||||
node_hash = RNS.Destination.hash_from_name_and_identity("nomadnetwork.node", node_identity)
|
||||
display_name = self.app.directory.alleged_display_str(node_hash)
|
||||
if display_name != None:
|
||||
@ -1797,7 +1843,16 @@ class LXMFPeerEntry(urwid.WidgetWrap):
|
||||
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")
|
||||
if peer.propagation_transfer_limit:
|
||||
txfer_limit = RNS.prettysize(peer.propagation_transfer_limit*1000)
|
||||
else:
|
||||
txfer_limit = "No"
|
||||
ar = round(peer.acceptance_rate*100, 2)
|
||||
peer_info_str = sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))
|
||||
peer_info_str += "\n "+str(peer.unhandled_message_count)+f" unhandled LXMs, {txfer_limit} sync limit\n"
|
||||
peer_info_str += f" {RNS.prettyspeed(peer.sync_transfer_rate)} STR, "
|
||||
peer_info_str += f"{RNS.prettyspeed(peer.link_establishment_rate)} LER, {ar}% AR\n"
|
||||
widget = ListEntry(peer_info_str)
|
||||
self.display_widget = urwid.AttrMap(widget, style, focus_style)
|
||||
self.display_widget.destination_hash = destination_hash
|
||||
super().__init__(self.display_widget)
|
||||
|
2483
nomadnet/vendor/configobj.py
vendored
2483
nomadnet/vendor/configobj.py
vendored
File diff suppressed because it is too large
Load Diff
4
setup.py
4
setup.py
@ -30,6 +30,6 @@ setuptools.setup(
|
||||
entry_points= {
|
||||
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
||||
},
|
||||
install_requires=["rns>=0.8.7", "lxmf>=0.5.8", "urwid>=2.4.4", "qrcode"],
|
||||
python_requires=">=3.6",
|
||||
install_requires=["rns>=0.9.4", "lxmf>=0.6.3", "urwid>=2.6.16", "qrcode"],
|
||||
python_requires=">=3.7",
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user