mirror of
https://github.com/markqvist/NomadNet.git
synced 2025-12-17 23:04:24 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81f65e3453 | ||
|
|
34b3987ded | ||
|
|
22a7acf259 | ||
|
|
919a146da1 | ||
|
|
f0a4efa28b | ||
|
|
3d0043499c | ||
|
|
b6e6c4bd3d | ||
|
|
d06e1d3f1b | ||
|
|
02f9a5a760 |
24
README.md
24
README.md
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.3.5"
|
__version__ = "0.3.6"
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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):
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -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",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user