mirror of
https://github.com/markqvist/NomadNet.git
synced 2025-12-17 06:44:21 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29feb1cab1 | ||
|
|
73fffe519a | ||
|
|
22f18324fc | ||
|
|
0affe9f283 | ||
|
|
dfd87a2119 | ||
|
|
c2f0f969fb | ||
|
|
9244f00e21 | ||
|
|
b1b2a2a302 | ||
|
|
b08ae0cf02 | ||
|
|
730c17c981 | ||
|
|
15a4ec2af9 | ||
|
|
08a9225cc9 |
31
README.md
31
README.md
@@ -1,5 +1,4 @@
|
|||||||
Nomad Network - Communicate Freely
|
# Nomad Network - Communicate Freely
|
||||||
==========
|
|
||||||
|
|
||||||
Off-grid, resilient mesh communication with strong encryption, forward secrecy and extreme privacy.
|
Off-grid, resilient mesh communication with strong encryption, forward secrecy and extreme privacy.
|
||||||
|
|
||||||
@@ -26,11 +25,6 @@ If you'd rather want to use an LXMF client with a graphical user interface, you
|
|||||||
## Current Status
|
## Current Status
|
||||||
The current version of the program should be considered a beta release. The program works well, but there will most probably be bugs and possibly sub-optimal performance in some scenarios. On the other hand, this is the ideal time to have an influence on the direction of the development of Nomad Network. To do so, join the discussion, report bugs and request features here on the GitHub project.
|
The current version of the program should be considered a beta release. The program works well, but there will most probably be bugs and possibly sub-optimal performance in some scenarios. On the other hand, this is the ideal time to have an influence on the direction of the development of Nomad Network. To do so, join the discussion, report bugs and request features here on the GitHub project.
|
||||||
|
|
||||||
### Feature roadmap
|
|
||||||
- Network-wide propagated bulletins and discussion threads
|
|
||||||
- Collaborative maps and geospatial information sharing
|
|
||||||
- Facilitation of trade and barter
|
|
||||||
|
|
||||||
## How do I get started?
|
## How do I get started?
|
||||||
The easiest way to install Nomad Network is via pip:
|
The easiest way to install Nomad Network is via pip:
|
||||||
|
|
||||||
@@ -119,6 +113,29 @@ You can help support the continued development of open, free and private communi
|
|||||||
```
|
```
|
||||||
- Ko-Fi: https://ko-fi.com/markqvist
|
- Ko-Fi: https://ko-fi.com/markqvist
|
||||||
|
|
||||||
|
## Development Roadmap
|
||||||
|
|
||||||
|
- New major features
|
||||||
|
- Network-wide propagated bulletins and discussion threads
|
||||||
|
- Collaborative maps and geospatial information sharing
|
||||||
|
- Facilitation of trade and barter
|
||||||
|
- Minor improvements and fixes
|
||||||
|
- Link status (RSSI and SNR) in conversation or conv list
|
||||||
|
- Ctrl-M shorcut for jumping to menu
|
||||||
|
- Share node with other users / send node info to user
|
||||||
|
- Fix internal editor failing on some OSes with no "editor" alias
|
||||||
|
- Possibly add a required-width header
|
||||||
|
- Improve browser handling of remote link close
|
||||||
|
- Better navigation handling when requests fail (also because of closed links)
|
||||||
|
- Retry failed messages mechanism
|
||||||
|
- Re-arrange buttons to be more consistent
|
||||||
|
- Input field for pages
|
||||||
|
- Post mechanism
|
||||||
|
- Term compatibility notice in readme
|
||||||
|
- Selected icon in conversation list
|
||||||
|
- Possibly a Search Local Nodes function
|
||||||
|
- Possibly add via entry in node info box, next to distance
|
||||||
|
|
||||||
## Caveat Emptor
|
## Caveat Emptor
|
||||||
Nomad Network is beta software, and should be considered as such. While it has been built with cryptography best-practices very foremost in mind, it _has not_ been externally security audited, and there could very well be privacy-breaking bugs. If you want to help out, or help sponsor an audit, please do get in touch.
|
Nomad Network is beta software, and should be considered as such. While it has been built with cryptography best-practices very foremost in mind, it _has not_ been externally security audited, and there could very well be privacy-breaking bugs. If you want to help out, or help sponsor an audit, please do get in touch.
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,35 @@ class Conversation:
|
|||||||
RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE)
|
RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def paper_output(self, content="", title=""):
|
||||||
|
if self.send_destination:
|
||||||
|
try:
|
||||||
|
dest = self.send_destination
|
||||||
|
source = self.app.lxmf_destination
|
||||||
|
desired_method = LXMF.LXMessage.PAPER
|
||||||
|
|
||||||
|
lxm = LXMF.LXMessage(dest, source, content, title=title, desired_method=desired_method)
|
||||||
|
qr_code = lxm.as_qr()
|
||||||
|
qr_tmp_path = self.app.tmpfilespath+"/"+str(RNS.hexrep(lxm.hash, delimit=False))
|
||||||
|
qr_code.save(qr_tmp_path)
|
||||||
|
|
||||||
|
print_result = self.app.print_file(qr_tmp_path)
|
||||||
|
os.unlink(qr_tmp_path)
|
||||||
|
|
||||||
|
if print_result:
|
||||||
|
message_path = Conversation.ingest(lxm, self.app, originator=True)
|
||||||
|
self.messages.append(ConversationMessage(message_path))
|
||||||
|
|
||||||
|
return print_result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("An error occurred while generating paper message, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE)
|
||||||
|
return False
|
||||||
|
|
||||||
def message_notification(self, message):
|
def message_notification(self, message):
|
||||||
if message.state == LXMF.LXMessage.FAILED and hasattr(message, "try_propagation_on_fail") and message.try_propagation_on_fail:
|
if message.state == LXMF.LXMessage.FAILED and hasattr(message, "try_propagation_on_fail") and message.try_propagation_on_fail:
|
||||||
RNS.log("Direct delivery of "+str(message)+" failed. Retrying as propagated message.", RNS.LOG_VERBOSE)
|
RNS.log("Direct delivery of "+str(message)+" failed. Retrying as propagated message.", RNS.LOG_VERBOSE)
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class NomadNetworkApp:
|
|||||||
self.conversationpath = self.configdir+"/storage/conversations"
|
self.conversationpath = self.configdir+"/storage/conversations"
|
||||||
self.directorypath = self.configdir+"/storage/directory"
|
self.directorypath = self.configdir+"/storage/directory"
|
||||||
self.peersettingspath = self.configdir+"/storage/peersettings"
|
self.peersettingspath = self.configdir+"/storage/peersettings"
|
||||||
|
self.tmpfilespath = self.configdir+"/storage/tmp"
|
||||||
|
|
||||||
self.pagespath = self.configdir+"/storage/pages"
|
self.pagespath = self.configdir+"/storage/pages"
|
||||||
self.filespath = self.configdir+"/storage/files"
|
self.filespath = self.configdir+"/storage/files"
|
||||||
@@ -145,6 +146,11 @@ class NomadNetworkApp:
|
|||||||
if not os.path.isdir(self.cachepath):
|
if not os.path.isdir(self.cachepath):
|
||||||
os.makedirs(self.cachepath)
|
os.makedirs(self.cachepath)
|
||||||
|
|
||||||
|
if not os.path.isdir(self.tmpfilespath):
|
||||||
|
os.makedirs(self.tmpfilespath)
|
||||||
|
else:
|
||||||
|
self.clear_tmp_dir()
|
||||||
|
|
||||||
if os.path.isfile(self.configpath):
|
if os.path.isfile(self.configpath):
|
||||||
try:
|
try:
|
||||||
self.config = ConfigObj(self.configpath)
|
self.config = ConfigObj(self.configpath)
|
||||||
@@ -514,6 +520,26 @@ class NomadNetworkApp:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def print_file(self, filename):
|
||||||
|
print_command = self.print_command+" "+filename
|
||||||
|
|
||||||
|
try:
|
||||||
|
return_code = subprocess.call(shlex.split(print_command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("An error occurred while executing print command: "+str(print_command), RNS.LOG_ERROR)
|
||||||
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if return_code == 0:
|
||||||
|
RNS.log("Successfully printed "+str(filename)+" using print command: "+print_command, RNS.LOG_DEBUG)
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
RNS.log("Printing "+str(filename)+" failed using print command: "+print_command, RNS.LOG_DEBUG)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def print_message(self, message, received = None):
|
def print_message(self, message, received = None):
|
||||||
try:
|
try:
|
||||||
template = self.printing_template_msg
|
template = self.printing_template_msg
|
||||||
@@ -547,8 +573,7 @@ class NomadNetworkApp:
|
|||||||
f.write(output.encode("utf-8"))
|
f.write(output.encode("utf-8"))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
print_command = "lp -d thermal -o cpi=16 -o lpi=8 "+filename
|
self.print_file(filename)
|
||||||
return_code = subprocess.call(shlex.split(print_command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
|
|
||||||
@@ -576,6 +601,12 @@ class NomadNetworkApp:
|
|||||||
if os.path.isfile(self.conversationpath + "/" + source_hash + "/unread"):
|
if os.path.isfile(self.conversationpath + "/" + source_hash + "/unread"):
|
||||||
os.unlink(self.conversationpath + "/" + source_hash + "/unread")
|
os.unlink(self.conversationpath + "/" + source_hash + "/unread")
|
||||||
|
|
||||||
|
def clear_tmp_dir(self):
|
||||||
|
if os.path.isdir(self.tmpfilespath):
|
||||||
|
for file in os.listdir(self.tmpfilespath):
|
||||||
|
fpath = self.tmpfilespath+"/"+file
|
||||||
|
os.unlink(fpath)
|
||||||
|
|
||||||
def createDefaultConfig(self):
|
def createDefaultConfig(self):
|
||||||
self.config = ConfigObj(__default_nomadnet_config__)
|
self.config = ConfigObj(__default_nomadnet_config__)
|
||||||
self.config.filename = self.configpath
|
self.config.filename = self.configpath
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.2.6"
|
__version__ = "0.2.8"
|
||||||
|
|||||||
@@ -121,7 +121,9 @@ GLYPHS = {
|
|||||||
("decoration_menu", " +", " +", " \uf93a"),
|
("decoration_menu", " +", " +", " \uf93a"),
|
||||||
("unread_menu", " !", " \u2709", urm_char),
|
("unread_menu", " !", " \u2709", urm_char),
|
||||||
("globe", "", "", "\uf484"),
|
("globe", "", "", "\uf484"),
|
||||||
("sent", "/\\", "\u2191", "\ufbf4")
|
("sent", "/\\", "\u2191", "\ufbf4"),
|
||||||
|
("papermsg", "P", "\u25a4", "\uf719"),
|
||||||
|
("qrcode", "QR", "\u25a4", "\uf029"),
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextUI:
|
class TextUI:
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ class ConversationListDisplayShortcuts():
|
|||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
self.widget = urwid.AttrMap(urwid.Text("[C-e] Peer Info [C-x] Delete [C-r] Sync [C-n] New [C-g] Fullscreen"), "shortcutbar")
|
self.widget = urwid.AttrMap(urwid.Text("[C-e] Peer Info [C-x] Delete [C-r] Sync [C-n] New [C-u] Ingest URI [C-g] Fullscreen"), "shortcutbar")
|
||||||
|
|
||||||
class ConversationDisplayShortcuts():
|
class ConversationDisplayShortcuts():
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
self.widget = urwid.AttrMap(urwid.Text("[C-d] Send [C-k] Clear [C-w] Close [C-t] Title [C-p] Purge [C-x] Clear History [C-o] Sort"), "shortcutbar")
|
self.widget = urwid.AttrMap(urwid.Text("[C-d] Send [C-p] Paper Msg [C-t] Title [C-k] Clear [C-w] Close [C-u] Purge [C-x] Clear History [C-o] Sort"), "shortcutbar")
|
||||||
|
|
||||||
class ConversationsArea(urwid.LineBox):
|
class ConversationsArea(urwid.LineBox):
|
||||||
def keypress(self, size, key):
|
def keypress(self, size, key):
|
||||||
@@ -30,6 +30,8 @@ class ConversationsArea(urwid.LineBox):
|
|||||||
self.delegate.delete_selected_conversation()
|
self.delegate.delete_selected_conversation()
|
||||||
elif key == "ctrl n":
|
elif key == "ctrl n":
|
||||||
self.delegate.new_conversation()
|
self.delegate.new_conversation()
|
||||||
|
elif key == "ctrl u":
|
||||||
|
self.delegate.ingest_lxm_uri()
|
||||||
elif key == "ctrl r":
|
elif key == "ctrl r":
|
||||||
self.delegate.sync_conversations()
|
self.delegate.sync_conversations()
|
||||||
elif key == "ctrl g":
|
elif key == "ctrl g":
|
||||||
@@ -330,6 +332,110 @@ class ConversationsDisplay():
|
|||||||
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||||
self.columns_widget.contents[0] = (overlay, options)
|
self.columns_widget.contents[0] = (overlay, options)
|
||||||
|
|
||||||
|
def ingest_lxm_uri(self):
|
||||||
|
self.dialog_open = True
|
||||||
|
lxm_uri = ""
|
||||||
|
e_uri = urwid.Edit(caption="URI : ",edit_text=lxm_uri)
|
||||||
|
|
||||||
|
def dismiss_dialog(sender):
|
||||||
|
self.update_conversation_list()
|
||||||
|
self.dialog_open = False
|
||||||
|
|
||||||
|
def confirmed(sender):
|
||||||
|
try:
|
||||||
|
local_delivery_signal = "local_delivery_occurred"
|
||||||
|
duplicate_signal = "duplicate_lxm"
|
||||||
|
lxm_uri = e_uri.get_edit_text()
|
||||||
|
|
||||||
|
ingest_result = self.app.message_router.ingest_lxm_uri(
|
||||||
|
lxm_uri,
|
||||||
|
signal_local_delivery=local_delivery_signal,
|
||||||
|
signal_duplicate=duplicate_signal
|
||||||
|
)
|
||||||
|
|
||||||
|
if ingest_result == False:
|
||||||
|
raise ValueError("The URI contained no decodable messages")
|
||||||
|
|
||||||
|
elif ingest_result == local_delivery_signal:
|
||||||
|
rdialog_pile = urwid.Pile([
|
||||||
|
urwid.Text("Message was decoded, decrypted successfully, and added to your conversation list."),
|
||||||
|
urwid.Text(""),
|
||||||
|
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))])
|
||||||
|
])
|
||||||
|
rdialog_pile.error_display = False
|
||||||
|
|
||||||
|
rdialog = DialogLineBox(rdialog_pile, title="Ingest message URI")
|
||||||
|
rdialog.delegate = self
|
||||||
|
bottom = self.listbox
|
||||||
|
|
||||||
|
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2)
|
||||||
|
|
||||||
|
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||||
|
self.columns_widget.contents[0] = (roverlay, options)
|
||||||
|
|
||||||
|
elif ingest_result == duplicate_signal:
|
||||||
|
rdialog_pile = urwid.Pile([
|
||||||
|
urwid.Text("The decoded message has already been processed by the LXMF Router, and will not be ingested again."),
|
||||||
|
urwid.Text(""),
|
||||||
|
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))])
|
||||||
|
])
|
||||||
|
rdialog_pile.error_display = False
|
||||||
|
|
||||||
|
rdialog = DialogLineBox(rdialog_pile, title="Ingest message URI")
|
||||||
|
rdialog.delegate = self
|
||||||
|
bottom = self.listbox
|
||||||
|
|
||||||
|
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2)
|
||||||
|
|
||||||
|
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||||
|
self.columns_widget.contents[0] = (roverlay, options)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.app.enable_node:
|
||||||
|
propagation_text = "The decoded message was not addressed to this LXMF address, but has been added to the propagation node queues, and will be distributed on the propagation network."
|
||||||
|
else:
|
||||||
|
propagation_text = "The decoded message was not addressed to this LXMF address, and has been discarded."
|
||||||
|
|
||||||
|
rdialog_pile = urwid.Pile([
|
||||||
|
urwid.Text(propagation_text),
|
||||||
|
urwid.Text(""),
|
||||||
|
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))])
|
||||||
|
])
|
||||||
|
rdialog_pile.error_display = False
|
||||||
|
|
||||||
|
rdialog = DialogLineBox(rdialog_pile, title="Ingest message URI")
|
||||||
|
rdialog.delegate = self
|
||||||
|
bottom = self.listbox
|
||||||
|
|
||||||
|
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2)
|
||||||
|
|
||||||
|
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||||
|
self.columns_widget.contents[0] = (roverlay, options)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Could not ingest LXM URI. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
|
||||||
|
if not dialog_pile.error_display:
|
||||||
|
dialog_pile.error_display = True
|
||||||
|
options = dialog_pile.options(height_type="pack")
|
||||||
|
dialog_pile.contents.append((urwid.Text(""), options))
|
||||||
|
dialog_pile.contents.append((urwid.Text(("error_text", "Could ingest LXM from URI data. Check your input."), align="center"), options))
|
||||||
|
|
||||||
|
dialog_pile = urwid.Pile([
|
||||||
|
e_uri,
|
||||||
|
urwid.Text(""),
|
||||||
|
urwid.Columns([("weight", 0.45, urwid.Button("Ingest", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Back", on_press=dismiss_dialog))])
|
||||||
|
])
|
||||||
|
dialog_pile.error_display = False
|
||||||
|
|
||||||
|
dialog = DialogLineBox(dialog_pile, title="Ingest message URI")
|
||||||
|
dialog.delegate = self
|
||||||
|
bottom = self.listbox
|
||||||
|
|
||||||
|
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2)
|
||||||
|
|
||||||
|
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||||
|
self.columns_widget.contents[0] = (overlay, options)
|
||||||
|
|
||||||
def delete_conversation(self, source_hash):
|
def delete_conversation(self, source_hash):
|
||||||
if source_hash in ConversationsDisplay.cached_conversation_widgets:
|
if source_hash in ConversationsDisplay.cached_conversation_widgets:
|
||||||
conversation = ConversationsDisplay.cached_conversation_widgets[source_hash]
|
conversation = ConversationsDisplay.cached_conversation_widgets[source_hash]
|
||||||
@@ -636,6 +742,8 @@ class MessageEdit(urwid.Edit):
|
|||||||
def keypress(self, size, key):
|
def keypress(self, size, key):
|
||||||
if key == "ctrl d":
|
if key == "ctrl d":
|
||||||
self.delegate.send_message()
|
self.delegate.send_message()
|
||||||
|
elif key == "ctrl p":
|
||||||
|
self.delegate.paper_message()
|
||||||
elif key == "ctrl k":
|
elif key == "ctrl k":
|
||||||
self.delegate.clear_editor()
|
self.delegate.clear_editor()
|
||||||
elif key == "up":
|
elif key == "up":
|
||||||
@@ -800,7 +908,7 @@ class ConversationWidget(urwid.WidgetWrap):
|
|||||||
self.toggle_focus_area()
|
self.toggle_focus_area()
|
||||||
elif key == "ctrl w":
|
elif key == "ctrl w":
|
||||||
self.close()
|
self.close()
|
||||||
elif key == "ctrl p":
|
elif key == "ctrl u":
|
||||||
self.conversation.purge_failed()
|
self.conversation.purge_failed()
|
||||||
self.conversation_changed(None)
|
self.conversation_changed(None)
|
||||||
elif key == "ctrl t":
|
elif key == "ctrl t":
|
||||||
@@ -860,6 +968,34 @@ class ConversationWidget(urwid.WidgetWrap):
|
|||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def paper_message(self):
|
||||||
|
content = self.content_editor.get_edit_text()
|
||||||
|
title = self.title_editor.get_edit_text()
|
||||||
|
if not content == "":
|
||||||
|
if self.conversation.paper_output(content, title):
|
||||||
|
self.clear_editor()
|
||||||
|
else:
|
||||||
|
self.paper_message_failed()
|
||||||
|
|
||||||
|
def paper_message_failed(self):
|
||||||
|
def dismiss_dialog(sender):
|
||||||
|
self.dialog_open = False
|
||||||
|
self.conversation_changed(None)
|
||||||
|
|
||||||
|
dialog = DialogLineBox(
|
||||||
|
urwid.Pile([
|
||||||
|
urwid.Text("Could not output paper message,\ncheck your settings. See the log\nfile for any error messages.\n", align="center"),
|
||||||
|
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))])
|
||||||
|
]), title="!"
|
||||||
|
)
|
||||||
|
dialog.delegate = self
|
||||||
|
bottom = self.messagelist
|
||||||
|
|
||||||
|
overlay = urwid.Overlay(dialog, bottom, align="center", width=34, valign="middle", height="pack", left=2, right=2)
|
||||||
|
|
||||||
|
self.frame.contents["body"] = (overlay, self.frame.options())
|
||||||
|
self.frame.set_focus("body")
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.delegate.close_conversation(self)
|
self.delegate.close_conversation(self)
|
||||||
|
|
||||||
@@ -890,6 +1026,9 @@ class LXMessageWidget(urwid.WidgetWrap):
|
|||||||
elif message.lxm.method == LXMF.LXMessage.PROPAGATED and message.lxm.state == LXMF.LXMessage.SENT:
|
elif message.lxm.method == LXMF.LXMessage.PROPAGATED and message.lxm.state == LXMF.LXMessage.SENT:
|
||||||
header_style = "msg_header_propagated"
|
header_style = "msg_header_propagated"
|
||||||
title_string = g["sent"]+" "+title_string
|
title_string = g["sent"]+" "+title_string
|
||||||
|
elif message.lxm.method == LXMF.LXMessage.PAPER and message.lxm.state == LXMF.LXMessage.PAPER:
|
||||||
|
header_style = "msg_header_propagated"
|
||||||
|
title_string = g["papermsg"]+" "+title_string
|
||||||
elif message.lxm.state == LXMF.LXMessage.SENT:
|
elif message.lxm.state == LXMF.LXMessage.SENT:
|
||||||
header_style = "msg_header_sent"
|
header_style = "msg_header_sent"
|
||||||
title_string = g["sent"]+" "+title_string
|
title_string = g["sent"]+" "+title_string
|
||||||
|
|||||||
@@ -894,7 +894,7 @@ class NodeActiveConnections(urwid.WidgetWrap):
|
|||||||
if self.app.node != None:
|
if self.app.node != None:
|
||||||
self.stat_string = str(len(self.app.node.destination.links))
|
self.stat_string = str(len(self.app.node.destination.links))
|
||||||
|
|
||||||
self.display_widget.set_text("Conneced Now : "+self.stat_string)
|
self.display_widget.set_text("Connected Now : "+self.stat_string)
|
||||||
|
|
||||||
def update_stat_callback(self, loop=None, user_data=None):
|
def update_stat_callback(self, loop=None, user_data=None):
|
||||||
self.update_stat()
|
self.update_stat()
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -23,6 +23,6 @@ setuptools.setup(
|
|||||||
entry_points= {
|
entry_points= {
|
||||||
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
||||||
},
|
},
|
||||||
install_requires=['rns>=0.4.0', 'lxmf>=0.2.3', 'urwid>=2.1.2'],
|
install_requires=["rns>=0.4.2", "lxmf>=0.2.6", "urwid>=2.1.2", "qrcode"],
|
||||||
python_requires='>=3.6',
|
python_requires=">=3.6",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user