Compare commits

...

9 Commits
0.3.5 ... 0.3.6

Author SHA1 Message Date
Mark Qvist
81f65e3453 Updated install section 2023-08-15 11:13:31 +02:00
Mark Qvist
34b3987ded Added error handling to micron parser mouse event translator. Fixes #32. 2023-08-14 18:02:39 +02:00
Mark Qvist
22a7acf259 Added compact announce stream option 2023-08-13 21:54:35 +02:00
Mark Qvist
919a146da1 Added alternative save node hotkey. Added save hotkey to hotkeys list. Closes #31. 2023-08-13 20:55:35 +02:00
Mark Qvist
f0a4efa28b Added alternative save node hotkey. Added save hotkey to hotkeys list. 2023-08-13 20:54:29 +02:00
Mark Qvist
3d0043499c Added error handling to lxmf.delivery announce handler. Fixes #30. Hopefully. 2023-08-13 20:37:44 +02:00
Mark Qvist
b6e6c4bd3d Updated version and dependencies 2023-06-13 19:57:49 +02:00
markqvist
d06e1d3f1b Merge pull request #27 from Swissbandit/patch-1
Update Guide.py
2023-04-02 10:21:37 +02:00
Swissbandit
02f9a5a760 Update Guide.py
fixed typo redundant .mu
2023-03-29 16:44:37 +02:00
9 changed files with 134 additions and 47 deletions

View File

@@ -30,7 +30,29 @@ The easiest way to install Nomad Network is via pip:
```bash ```bash
# Install Nomad Network and dependencies # Install Nomad Network and dependencies
pip3 install nomadnet pip install nomadnet
# Run the client
nomadnet
# Or alternatively run as a daemon, with no user interface
nomadnet --daemon
# List options
nomadnet --help
```
If you are using an operating system that blocks normal user package installation via `pip`, you can use the `pipx` tool to install Nomad Network instead:
```bash
# Install Nomad Network
pipx install nomadnet
# Optionally install Reticulum utilities
pipx install rns
# Optionally install standalone LXMF utilities
pipx install lxmf
# Run the client # Run the client
nomadnet nomadnet

View File

@@ -34,16 +34,21 @@ class Directory:
aspect_filter = "nomadnetwork.node" aspect_filter = "nomadnetwork.node"
@staticmethod @staticmethod
def received_announce(destination_hash, announced_identity, app_data): def received_announce(destination_hash, announced_identity, app_data):
app = nomadnet.NomadNetworkApp.get_shared_instance() try:
app = nomadnet.NomadNetworkApp.get_shared_instance()
if not destination_hash in app.ignored_list: if not destination_hash in app.ignored_list:
associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity) associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity)
app.directory.node_announce_received(destination_hash, app_data, associated_peer) app.directory.node_announce_received(destination_hash, app_data, associated_peer)
app.autoselect_propagation_node() app.autoselect_propagation_node()
else: else:
RNS.log("Ignored announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG) RNS.log("Ignored announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Error while evaluating LXMF destination announce, ignoring announce.", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
def __init__(self, app): def __init__(self, app):
@@ -113,6 +118,19 @@ class Directory:
def lxmf_announce_received(self, source_hash, app_data): def lxmf_announce_received(self, source_hash, app_data):
if app_data != None: 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)
timestamp = time.time() timestamp = time.time()
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "peer")) self.announce_stream.insert(0, (timestamp, source_hash, app_data, "peer"))
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH: while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
@@ -123,6 +141,19 @@ class Directory:
def node_announce_received(self, source_hash, app_data, associated_peer): def node_announce_received(self, source_hash, app_data, associated_peer):
if app_data != None: 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)
timestamp = time.time() timestamp = time.time()
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "node")) self.announce_stream.insert(0, (timestamp, source_hash, app_data, "node"))
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH: while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
@@ -150,6 +181,19 @@ class Directory:
break break
if not found_node: 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)
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() timestamp = time.time()
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "pn")) self.announce_stream.insert(0, (timestamp, source_hash, app_data, "pn"))
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH: while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:

View File

@@ -126,6 +126,7 @@ class NomadNetworkApp:
self.periodic_lxmf_sync = True self.periodic_lxmf_sync = True
self.lxmf_sync_interval = 360*60 self.lxmf_sync_interval = 360*60
self.lxmf_sync_limit = 8 self.lxmf_sync_limit = 8
self.compact_stream = False
if not os.path.isdir(self.storagepath): if not os.path.isdir(self.storagepath):
os.makedirs(self.storagepath) os.makedirs(self.storagepath)
@@ -698,6 +699,10 @@ class NomadNetworkApp:
else: else:
self.lxmf_sync_limit = None self.lxmf_sync_limit = None
if option == "compact_announce_stream":
value = self.config["client"].as_bool(option)
self.compact_stream = value
if option == "user_interface": if option == "user_interface":
value = value.lower() value = value.lower()
if value == "none": if value == "none":
@@ -929,6 +934,12 @@ lxmf_sync_interval = 360
# the limit, and download everything every time. # the limit, and download everything every time.
lxmf_sync_limit = 8 lxmf_sync_limit = 8
# The announce stream will only show one entry
# per destination or node by default. You can
# change this to show as many announces as have
# been received, for every destination.
compact_announce_stream = yes
[textui] [textui]
# Amount of time to show intro screen # Amount of time to show intro screen

View File

@@ -1 +1 @@
__version__ = "0.3.5" __version__ = "0.3.6"

View File

@@ -23,6 +23,8 @@ class BrowserFrame(urwid.Frame):
self.delegate.url_dialog() self.delegate.url_dialog()
elif key == "ctrl s": elif key == "ctrl s":
self.delegate.save_node_dialog() self.delegate.save_node_dialog()
elif key == "ctrl b":
self.delegate.save_node_dialog()
elif key == "ctrl g": elif key == "ctrl g":
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.toggle_fullscreen() nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.toggle_fullscreen()
elif self.get_focus() == "body": elif self.get_focus() == "body":

View File

@@ -335,7 +335,7 @@ By default, you can find the examples in `!~/.nomadnetwork/examples`!. If you bu
Sometimes, you don't want everyone to be able to view certain pages or execute certain scripts. In such cases, you can use `*authentication`* to control who gets to run certain requests. Sometimes, you don't want everyone to be able to view certain pages or execute certain scripts. In such cases, you can use `*authentication`* to control who gets to run certain requests.
To enable authentication for any page, simply add a new file to your pages directory with ".allowed" added to the file-name of the page. If your page is named "secret_page.mu", just add a file named "secret_page.mu.allowed". To enable authentication for any page, simply add a new file to your pages directory with ".allowed" added to the file-name of the page. If your page is named "secret_page.mu", just add a file named "secret_page.allowed".
For each user allowed to access the page, add a line to this file, containing the hash of that users primary identity. Users can find their own identity hash in the `![ Network ]`! part of the program, under `!Local Peer Info`!. If you want to allow access for three different users, your file would look like this: For each user allowed to access the page, add a line to this file, containing the hash of that users primary identity. Users can find their own identity hash in the `![ Network ]`! part of the program, under `!Local Peer Info`!. If you want to allow access for three different users, your file would look like this:
@@ -506,6 +506,12 @@ The number of minutes between each automatic sync. The default is equal to 6 hou
On low-bandwidth networks, it can be useful to limit the amount of messages downloaded in each sync. The default is 8. Set to 0 to download all available messages every time a sync occurs. On low-bandwidth networks, it can be useful to limit the amount of messages downloaded in each sync. The default is 8. Set to 0 to download all available messages every time a sync occurs.
< <
>>>
`!compact_announce_stream = yes`!
>>>>
With this option enabled, Nomad Network will only display one entry in the announce stream per destination. Older announces are culled when a new one arrives.
<
>> Text UI Section >> 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: 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:

View File

@@ -751,41 +751,45 @@ class LinkableText(urwid.Text):
return x, y return x, y
def mouse_event(self, size, event, button, x, y, focus): def mouse_event(self, size, event, button, x, y, focus):
if button != 1 or not is_mouse_press(event): try:
return False if button != 1 or not is_mouse_press(event):
else: return False
(maxcol,) = size
translation = self.get_line_translation(maxcol)
line_offset = 0
if self.align == "center":
line_offset = translation[y][1][1]-translation[y][0][0]
if x < translation[y][0][0]:
x = translation[y][0][0]
if x > translation[y][1][0]+translation[y][0][0]:
x = translation[y][1][0]+translation[y][0][0]
elif self.align == "right":
line_offset = translation[y][1][1]-translation[y][0][0]
if x < translation[y][0][0]:
x = translation[y][0][0]
else: else:
line_offset = translation[y][0][1] (maxcol,) = size
if x > translation[y][0][0]: translation = self.get_line_translation(maxcol)
x = translation[y][0][0] line_offset = 0
pos = line_offset+x if self.align == "center":
line_offset = translation[y][1][1]-translation[y][0][0]
if x < translation[y][0][0]:
x = translation[y][0][0]
self._cursor_position = pos if x > translation[y][1][0]+translation[y][0][0]:
item = self.find_item_at_pos(self._cursor_position) x = translation[y][1][0]+translation[y][0][0]
if item != None: elif self.align == "right":
if isinstance(item, LinkSpec): line_offset = translation[y][1][1]-translation[y][0][0]
self.handle_link(item.link_target, item.link_fields) if x < translation[y][0][0]:
x = translation[y][0][0]
self._invalidate() else:
self._emit("change") line_offset = translation[y][0][1]
if x > translation[y][0][0]:
x = translation[y][0][0]
pos = line_offset+x
self._cursor_position = pos
item = self.find_item_at_pos(self._cursor_position)
if item != None:
if isinstance(item, LinkSpec):
self.handle_link(item.link_target, item.link_fields)
self._invalidate()
self._emit("change")
return True
return True except Exception as e:
return False

View File

@@ -13,9 +13,7 @@ class NetworkDisplayShortcuts():
self.app = app self.app = app
g = app.ui.glyphs g = app.ui.glyphs
self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-g] Fullscreen"), "shortcutbar") self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-g] Fullscreen [C-s / C-b] Save Node"), "shortcutbar")
# "[C-"+g["arrow_u"]+g["arrow_d"]+"] Navigate Lists"
class DialogLineBox(urwid.LineBox): class DialogLineBox(urwid.LineBox):
def keypress(self, size, key): def keypress(self, size, key):

View File

@@ -30,6 +30,6 @@ setuptools.setup(
entry_points= { entry_points= {
'console_scripts': ['nomadnet=nomadnet.nomadnet:main'] 'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
}, },
install_requires=["rns>=0.4.9", "lxmf>=0.3.1", "urwid>=2.1.2", "qrcode"], install_requires=["rns>=0.5.7", "lxmf>=0.3.2", "urwid>=2.1.2", "qrcode"],
python_requires=">=3.6", python_requires=">=3.6",
) )