mirror of
https://github.com/liberatedsystems/openCom-Companion.git
synced 2024-11-22 05:20:36 +01:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
0d0fcaf572
14
README.md
14
README.md
@ -21,7 +21,7 @@ Sideband provides many useful and interesting functions, such as:
|
||||
- Geospatial awareness calculations.
|
||||
- Exchanging messages through **encrypted QR-codes on paper**, or through messages embedded directly in **lxm://** links.
|
||||
- Using **Android devices as impromptu Reticulum routers** (*Transport Instances*), for setting up or extending networks easily.
|
||||
- Remote **command execution and response engine**, with built-in commands, such as `ping`, `signal` reports and `echo`, and **full plug-ing expandability**.
|
||||
- Remote **command execution and response engine**, with built-in commands, such as `ping`, `signal` reports and `echo`, and **full plugin expandability**.
|
||||
- Remote **telemetry querying**, with strong, secure and cryptographically robust authentication and control.
|
||||
- **Plugin system** that allows you to easily **create your own commands**, services and telemetry sources.
|
||||
|
||||
@ -45,12 +45,12 @@ After the application is installed on your Android device, it is also possible t
|
||||
|
||||
## On Linux
|
||||
|
||||
On all Linux-based operating systems, Sideband is available as a `pipx`/`pip` package. This installation method **includes desktop integration**, so that Sideband will show up in your applications menu and launchers.
|
||||
On all Linux-based operating systems, Sideband is available as a `pipx`/`pip` package. This installation method **includes desktop integration**, so that Sideband will show up in your applications menu and launchers. Below are install steps for the most common recent Linux distros. For Debian 11, see the end of this section.
|
||||
|
||||
You will first need to install a few dependencies for audio messaging and Codec2 support to work:
|
||||
|
||||
```bash
|
||||
# For Debian, Ubuntu and derivatives
|
||||
# For Debian (12+), Ubuntu (22.04+) and derivatives
|
||||
sudo apt install pipx python3-pyaudio python3-dev build-essential libopusfile0 portaudio19-dev codec2 xclip xsel
|
||||
|
||||
# For Manjaro and derivatives
|
||||
@ -112,6 +112,14 @@ pip install sbapp --no-dependencies
|
||||
# In the above case, you will still need to
|
||||
# manually install the RNS and LXMF dependencies:
|
||||
pip install rns lxmf
|
||||
|
||||
# Install Sideband on Debian 11 and derivatives:
|
||||
sudo apt install python3-pip python3-pyaudio python3-dev build-essential libopusfile0 portaudio19-dev codec2 xclip xsel
|
||||
pip install sbapp
|
||||
|
||||
# On Debian 11, run Sideband manually via the
|
||||
# terminal once to install desktop integration:
|
||||
python3 -m sbapp.main
|
||||
```
|
||||
|
||||
## On Raspberry Pi
|
||||
|
@ -10,7 +10,7 @@ source.exclude_patterns = app_storage/*,venv/*,Makefile,./Makefil*,requirements,
|
||||
|
||||
version.regex = __version__ = ['"](.*)['"]
|
||||
version.filename = %(source.dir)s/main.py
|
||||
android.numeric_version = 20240911
|
||||
android.numeric_version = 20240920
|
||||
|
||||
requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl,android,able_recipe
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
__debug_build__ = False
|
||||
__disable_shaders__ = False
|
||||
__version__ = "0.9.5"
|
||||
__version__ = "0.9.7"
|
||||
__variant__ = "beta"
|
||||
|
||||
import sys
|
||||
@ -303,6 +303,7 @@ class SidebandApp(MDApp):
|
||||
|
||||
self.final_load_completed = False
|
||||
self.service_last_available = 0
|
||||
self.closing_app = False
|
||||
|
||||
self.attach_path = None
|
||||
self.attach_type = None
|
||||
@ -988,6 +989,11 @@ class SidebandApp(MDApp):
|
||||
ok_button.bind(on_release=dl_ok)
|
||||
dialog.open()
|
||||
|
||||
def close_requested(self, *args):
|
||||
if not self.closing_app:
|
||||
self.quit_action(None)
|
||||
return True
|
||||
|
||||
def on_start(self):
|
||||
self.last_exit_event = time.time()
|
||||
self.root.ids.screen_manager.transition = self.slide_transition
|
||||
@ -997,6 +1003,7 @@ class SidebandApp(MDApp):
|
||||
EventLoop.window.bind(on_keyboard=self.keyboard_event)
|
||||
EventLoop.window.bind(on_key_down=self.keydown_event)
|
||||
EventLoop.window.bind(on_key_up=self.keyup_event)
|
||||
Window.bind(on_request_close=self.close_requested)
|
||||
|
||||
if __variant__ != "":
|
||||
variant_str = " "+__variant__
|
||||
@ -1241,6 +1248,7 @@ class SidebandApp(MDApp):
|
||||
self.root.ids.nav_drawer.set_state("closed")
|
||||
|
||||
def quit_action(self, sender):
|
||||
self.closing_app = True
|
||||
self.root.ids.nav_drawer.set_state("closed")
|
||||
self.sideband.should_persist_data()
|
||||
|
||||
@ -2760,8 +2768,9 @@ class SidebandApp(MDApp):
|
||||
pre = self.settings_screen.ids.settings_lxmf_sync_periodic.text
|
||||
self.settings_screen.ids.settings_lxmf_sync_periodic.text = "Auto sync every "+interval_text
|
||||
if save:
|
||||
self.sideband.config["lxmf_sync_interval"] = interval
|
||||
self.sideband.save_configuration()
|
||||
if (event == None or not hasattr(event, "button") or not event.button) or not "scroll" in event.button:
|
||||
self.sideband.config["lxmf_sync_interval"] = interval
|
||||
self.sideband.save_configuration()
|
||||
|
||||
def stamp_cost_change(sender=None, event=None, save=True):
|
||||
slider_val = int(self.settings_screen.ids.settings_lxmf_require_stamps_cost.value)
|
||||
@ -2774,7 +2783,8 @@ class SidebandApp(MDApp):
|
||||
if slider_val < 1:
|
||||
slider_val = 1
|
||||
self.sideband.config["lxmf_inbound_stamp_cost"] = slider_val
|
||||
self.sideband.save_configuration()
|
||||
if (event == None or not hasattr(event, "button") or not event.button) or not "scroll" in event.button:
|
||||
self.sideband.save_configuration()
|
||||
|
||||
self.settings_screen.ids.settings_lxmf_address.text = RNS.hexrep(self.sideband.lxmf_destination.hash, delimit=False)
|
||||
self.settings_screen.ids.settings_identity_hash.text = RNS.hexrep(self.sideband.lxmf_destination.identity.hash, delimit=False)
|
||||
@ -2991,35 +3001,35 @@ class SidebandApp(MDApp):
|
||||
self.widget_hide(self.connectivity_screen.ids.connectivity_transport_fields)
|
||||
|
||||
def con_collapse_local(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_local_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_local_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_tcp(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_tcp_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_tcp_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_i2p(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_i2p_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_i2p_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_bluetooth(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_bluetooth_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_bluetooth_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_rnode(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_rnode_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_rnode_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_modem(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_modem_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_modem_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_serial(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_serial_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_serial_fields, collapse)
|
||||
pass
|
||||
|
||||
def con_collapse_transport(collapse=True):
|
||||
# self.widget_hide(self.root.ids.connectivity_transport_fields, collapse)
|
||||
# self.widget_hide(self.connectivity_screen.ids.connectivity_transport_fields, collapse)
|
||||
pass
|
||||
|
||||
def save_connectivity(sender=None, event=None):
|
||||
@ -6132,7 +6142,7 @@ Thank you very much for using Free Communications Systems.
|
||||
self.root.ids.screen_manager.transition = self.slide_transition
|
||||
self.root.ids.screen_manager.transition.direction = direction
|
||||
|
||||
info = "The [b]Local Broadcasts[/b] feature will allow you to send and listen for local broadcast transmissions on connected radio, LoRa and WiFi interfaces.\n\n[b]Local Broadcasts[/b] makes it easy to establish public information exchange with anyone in direct radio range, or even with large areas far away using the [i]Remote Broadcast Repeater[/i] feature.\n\nThese features are not yet implemented in openCom Companion.\n\nWant it faster? Go to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project."
|
||||
info = "The [b]Local Broadcasts[/b] feature will allow you to send and listen for local broadcast transmissions on all connected interfaces.\n\n[b]Local Broadcasts[/b] makes it easy to establish public information exchange with anyone in direct radio range, or even with large areas far away using the [i]Remote Broadcast Repeater[/i] feature.\n\nThese features are not yet implemented in openCom Companion.\n\nWant it faster? Go to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project."
|
||||
if self.theme_cls.theme_style == "Dark":
|
||||
info = "[color=#"+dark_theme_text_color+"]"+info+"[/color]"
|
||||
self.broadcasts_screen.ids.broadcasts_info.text = info
|
||||
|
@ -96,9 +96,9 @@ class SidebandCore():
|
||||
TELEMETRY_INTERVAL = 60
|
||||
SERVICE_TELEMETRY_INTERVAL = 300
|
||||
|
||||
IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 6 # In seconds
|
||||
IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 3.5 # In seconds
|
||||
AUTO_ANNOUNCE_RANDOM_MIN = 90 # In minutes
|
||||
AUTO_ANNOUNCE_RANDOM_MAX = 480 # In minutes
|
||||
AUTO_ANNOUNCE_RANDOM_MAX = 300 # In minutes
|
||||
|
||||
DEFAULT_APPEARANCE = ["account", [0,0,0,1], [1,1,1,1]]
|
||||
|
||||
@ -110,11 +110,12 @@ class SidebandCore():
|
||||
# This reformats the new v0.5.0 announce data back to the expected format
|
||||
# for Sidebands database and other handling functions.
|
||||
dn = LXMF.display_name_from_app_data(app_data)
|
||||
sc = LXMF.stamp_cost_from_app_data(app_data)
|
||||
app_data = b""
|
||||
if dn != None:
|
||||
app_data = dn.encode("utf-8")
|
||||
|
||||
self.log_announce(destination_hash, app_data, dest_type=SidebandCore.aspect_filter)
|
||||
self.log_announce(destination_hash, app_data, dest_type=SidebandCore.aspect_filter, stamp_cost=sc)
|
||||
|
||||
def __init__(self, owner_app, config_path = None, is_service=False, is_client=False, android_app_dir=None, verbose=False, owner_service=None, service_context=None, is_daemon=False, load_config_only=False):
|
||||
self.is_service = is_service
|
||||
@ -154,6 +155,7 @@ class SidebandCore():
|
||||
self.service_stopped = False
|
||||
self.service_context = service_context
|
||||
self.owner_service = owner_service
|
||||
self.allow_service_dispatch = True
|
||||
self.version_str = ""
|
||||
|
||||
if config_path == None:
|
||||
@ -943,11 +945,12 @@ class SidebandCore():
|
||||
else:
|
||||
plyer.notification.notify(title, content, app_icon=self.icon_32)
|
||||
|
||||
def log_announce(self, dest, app_data, dest_type):
|
||||
def log_announce(self, dest, app_data, dest_type, stamp_cost=None):
|
||||
try:
|
||||
if app_data == None:
|
||||
app_data = b""
|
||||
RNS.log("Received "+str(dest_type)+" announce for "+RNS.prettyhexrep(dest)+" with data: "+app_data.decode("utf-8"), RNS.LOG_DEBUG)
|
||||
app_data = msgpack.packb([app_data, stamp_cost])
|
||||
RNS.log("Received "+str(dest_type)+" announce for "+RNS.prettyhexrep(dest)+" with data: "+str(app_data), RNS.LOG_DEBUG)
|
||||
self._db_save_announce(dest, app_data, dest_type)
|
||||
self.setstate("app.flags.new_announces", True)
|
||||
|
||||
@ -1142,9 +1145,11 @@ class SidebandCore():
|
||||
else:
|
||||
app_data = RNS.Identity.recall_app_data(context_dest)
|
||||
if app_data != None:
|
||||
return LXMF.display_name_from_app_data(app_data)+" "+RNS.prettyhexrep(context_dest)
|
||||
name_str = LXMF.display_name_from_app_data(app_data)
|
||||
addr_str = RNS.prettyhexrep(context_dest)
|
||||
return name_str+" "+addr_str
|
||||
else:
|
||||
return RNS.prettyhexrep(context_dest)
|
||||
return "Anonymous Peer "+RNS.prettyhexrep(context_dest)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
@ -1252,130 +1257,192 @@ class SidebandCore():
|
||||
else:
|
||||
self.setstate(f"telemetry.{RNS.hexrep(message.destination_hash, delimit=False)}.request_sending", False)
|
||||
|
||||
def _service_request_latest_telemetry(self, from_addr=None):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
else:
|
||||
if self.is_client:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
|
||||
self.rpc_connection.send({"request_latest_telemetry": {"from_addr": from_addr}})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while requesting latest telemetry over RPC: "+str(e), RNS.LOG_DEBUG)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def request_latest_telemetry(self, from_addr=None):
|
||||
if from_addr == None or from_addr == self.lxmf_destination.hash:
|
||||
return "no_address"
|
||||
else:
|
||||
if self.getstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending") == True:
|
||||
RNS.log("Not sending new telemetry request, since an earlier transfer is already in progress", RNS.LOG_DEBUG)
|
||||
return "in_progress"
|
||||
if self.allow_service_dispatch and self.is_client:
|
||||
try:
|
||||
return self._service_request_latest_telemetry(from_addr)
|
||||
|
||||
if from_addr != None:
|
||||
dest_identity = RNS.Identity.recall(from_addr)
|
||||
|
||||
if dest_identity == None:
|
||||
RNS.log("The identity for "+RNS.prettyhexrep(from_addr)+" could not be recalled. Requesting identity from network...", RNS.LOG_DEBUG)
|
||||
RNS.Transport.request_path(from_addr)
|
||||
return "destination_unknown"
|
||||
|
||||
else:
|
||||
now = time.time()
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if self.config["telemetry_use_propagation_only"] == True:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
request_timebase = self.getpersistent(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.timebase") or now - self.telemetry_request_max_history
|
||||
lxm_fields = { LXMF.FIELD_COMMANDS: [
|
||||
{Commands.TELEMETRY_REQUEST: request_timebase},
|
||||
]}
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, "", desired_method=desired_method, fields = lxm_fields, include_ticket=True)
|
||||
lxm.request_timebase = request_timebase
|
||||
lxm.register_delivery_callback(self.telemetry_request_finished)
|
||||
lxm.register_failed_callback(self.telemetry_request_finished)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["telemetry_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
RNS.log(f"Sending telemetry request with timebase {request_timebase}", RNS.LOG_DEBUG)
|
||||
self.setpersistent(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.last_request_attempt", time.time())
|
||||
self.setstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending", True)
|
||||
self.message_router.handle_outbound(lxm)
|
||||
return "sent"
|
||||
|
||||
else:
|
||||
except Exception as e:
|
||||
RNS.log("Error requesting latest telemetry: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
return "not_sent"
|
||||
|
||||
else:
|
||||
if from_addr == None or from_addr == self.lxmf_destination.hash:
|
||||
return "no_address"
|
||||
else:
|
||||
if self.getstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending") == True:
|
||||
RNS.log("Not sending new telemetry request, since an earlier transfer is already in progress", RNS.LOG_DEBUG)
|
||||
return "in_progress"
|
||||
|
||||
def send_latest_telemetry(self, to_addr=None, stream=None, is_authorized_telemetry_request=False):
|
||||
if to_addr == None or to_addr == self.lxmf_destination.hash:
|
||||
return "no_address"
|
||||
else:
|
||||
if to_addr == self.config["telemetry_collector"]:
|
||||
is_authorized_telemetry_request = True
|
||||
if from_addr != None:
|
||||
dest_identity = RNS.Identity.recall(from_addr)
|
||||
|
||||
if dest_identity == None:
|
||||
RNS.log("The identity for "+RNS.prettyhexrep(from_addr)+" could not be recalled. Requesting identity from network...", RNS.LOG_DEBUG)
|
||||
RNS.Transport.request_path(from_addr)
|
||||
return "destination_unknown"
|
||||
|
||||
if self.getstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending") == True:
|
||||
RNS.log("Not sending new telemetry update, since an earlier transfer is already in progress", RNS.LOG_DEBUG)
|
||||
return "in_progress"
|
||||
else:
|
||||
now = time.time()
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if self.config["telemetry_use_propagation_only"] == True:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
if (self.latest_packed_telemetry != None and self.latest_telemetry != None) or stream != None:
|
||||
dest_identity = RNS.Identity.recall(to_addr)
|
||||
|
||||
if dest_identity == None:
|
||||
RNS.log("The identity for "+RNS.prettyhexrep(to_addr)+" could not be recalled. Requesting identity from network...", RNS.LOG_DEBUG)
|
||||
RNS.Transport.request_path(to_addr)
|
||||
return "destination_unknown"
|
||||
request_timebase = self.getpersistent(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.timebase") or now - self.telemetry_request_max_history
|
||||
lxm_fields = { LXMF.FIELD_COMMANDS: [
|
||||
{Commands.TELEMETRY_REQUEST: request_timebase},
|
||||
]}
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, "", desired_method=desired_method, fields = lxm_fields, include_ticket=True)
|
||||
lxm.request_timebase = request_timebase
|
||||
lxm.register_delivery_callback(self.telemetry_request_finished)
|
||||
lxm.register_failed_callback(self.telemetry_request_finished)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["telemetry_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
RNS.log(f"Sending telemetry request with timebase {request_timebase}", RNS.LOG_DEBUG)
|
||||
self.setpersistent(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.last_request_attempt", time.time())
|
||||
self.setstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending", True)
|
||||
self.message_router.handle_outbound(lxm)
|
||||
return "sent"
|
||||
|
||||
else:
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if self.config["telemetry_use_propagation_only"] == True:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
return "not_sent"
|
||||
|
||||
lxm_fields = self.get_message_fields(to_addr, is_authorized_telemetry_request=is_authorized_telemetry_request, signal_already_sent=True)
|
||||
if lxm_fields == False and stream == None:
|
||||
return "already_sent"
|
||||
|
||||
if stream != None and len(stream) > 0:
|
||||
if lxm_fields == False:
|
||||
lxm_fields = {}
|
||||
lxm_fields[LXMF.FIELD_TELEMETRY_STREAM] = stream
|
||||
|
||||
if lxm_fields != None and lxm_fields != False and (LXMF.FIELD_TELEMETRY in lxm_fields or LXMF.FIELD_TELEMETRY_STREAM in lxm_fields):
|
||||
if LXMF.FIELD_TELEMETRY in lxm_fields:
|
||||
telemeter = Telemeter.from_packed(lxm_fields[LXMF.FIELD_TELEMETRY])
|
||||
telemetry_timebase = telemeter.read_all()["time"]["utc"]
|
||||
elif LXMF.FIELD_TELEMETRY_STREAM in lxm_fields:
|
||||
telemetry_timebase = 0
|
||||
for te in lxm_fields[LXMF.FIELD_TELEMETRY_STREAM]:
|
||||
ts = te[1]
|
||||
telemetry_timebase = max(telemetry_timebase, ts)
|
||||
|
||||
if telemetry_timebase > (self.getpersistent(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.last_send_success_timebase") or 0):
|
||||
lxm = LXMF.LXMessage(dest, source, "", desired_method=desired_method, fields = lxm_fields, include_ticket=self.is_trusted(to_addr))
|
||||
lxm.telemetry_timebase = telemetry_timebase
|
||||
lxm.register_delivery_callback(self.outbound_telemetry_finished)
|
||||
lxm.register_failed_callback(self.outbound_telemetry_finished)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["telemetry_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
RNS.log(f"Sending telemetry update with timebase {telemetry_timebase}", RNS.LOG_DEBUG)
|
||||
|
||||
self.setpersistent(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.last_send_attempt", time.time())
|
||||
self.setstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending", True)
|
||||
self.message_router.handle_outbound(lxm)
|
||||
return "sent"
|
||||
|
||||
else:
|
||||
RNS.log(f"Telemetry update with timebase {telemetry_timebase} was already successfully sent", RNS.LOG_DEBUG)
|
||||
return "already_sent"
|
||||
else:
|
||||
return "nothing_to_send"
|
||||
def _service_send_latest_telemetry(self, to_addr=None, stream=None, is_authorized_telemetry_request=False):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
else:
|
||||
if self.is_client:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
|
||||
self.rpc_connection.send({"send_latest_telemetry": {
|
||||
"to_addr": to_addr,
|
||||
"stream": stream,
|
||||
"is_authorized_telemetry_request": is_authorized_telemetry_request}
|
||||
})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending latest telemetry over RPC: "+str(e), RNS.LOG_DEBUG)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
else:
|
||||
RNS.log("A telemetry update was requested, but there was nothing to send.", RNS.LOG_WARNING)
|
||||
return "nothing_to_send"
|
||||
return False
|
||||
|
||||
def send_latest_telemetry(self, to_addr=None, stream=None, is_authorized_telemetry_request=False):
|
||||
if self.allow_service_dispatch and self.is_client:
|
||||
try:
|
||||
return self._service_send_latest_telemetry(to_addr, stream, is_authorized_telemetry_request)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error requesting latest telemetry: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
return "not_sent"
|
||||
|
||||
else:
|
||||
if to_addr == None or to_addr == self.lxmf_destination.hash:
|
||||
return "no_address"
|
||||
else:
|
||||
if to_addr == self.config["telemetry_collector"]:
|
||||
is_authorized_telemetry_request = True
|
||||
|
||||
if self.getstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending") == True:
|
||||
RNS.log("Not sending new telemetry update, since an earlier transfer is already in progress", RNS.LOG_DEBUG)
|
||||
return "in_progress"
|
||||
|
||||
if (self.latest_packed_telemetry != None and self.latest_telemetry != None) or stream != None:
|
||||
dest_identity = RNS.Identity.recall(to_addr)
|
||||
|
||||
if dest_identity == None:
|
||||
RNS.log("The identity for "+RNS.prettyhexrep(to_addr)+" could not be recalled. Requesting identity from network...", RNS.LOG_DEBUG)
|
||||
RNS.Transport.request_path(to_addr)
|
||||
return "destination_unknown"
|
||||
|
||||
else:
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if self.config["telemetry_use_propagation_only"] == True:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
lxm_fields = self.get_message_fields(to_addr, is_authorized_telemetry_request=is_authorized_telemetry_request, signal_already_sent=True)
|
||||
if lxm_fields == False and stream == None:
|
||||
return "already_sent"
|
||||
|
||||
if stream != None and len(stream) > 0:
|
||||
if lxm_fields == False:
|
||||
lxm_fields = {}
|
||||
lxm_fields[LXMF.FIELD_TELEMETRY_STREAM] = stream
|
||||
|
||||
if lxm_fields != None and lxm_fields != False and (LXMF.FIELD_TELEMETRY in lxm_fields or LXMF.FIELD_TELEMETRY_STREAM in lxm_fields):
|
||||
if LXMF.FIELD_TELEMETRY in lxm_fields:
|
||||
telemeter = Telemeter.from_packed(lxm_fields[LXMF.FIELD_TELEMETRY])
|
||||
telemetry_timebase = telemeter.read_all()["time"]["utc"]
|
||||
elif LXMF.FIELD_TELEMETRY_STREAM in lxm_fields:
|
||||
telemetry_timebase = 0
|
||||
for te in lxm_fields[LXMF.FIELD_TELEMETRY_STREAM]:
|
||||
ts = te[1]
|
||||
telemetry_timebase = max(telemetry_timebase, ts)
|
||||
|
||||
if telemetry_timebase > (self.getpersistent(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.last_send_success_timebase") or 0):
|
||||
lxm = LXMF.LXMessage(dest, source, "", desired_method=desired_method, fields = lxm_fields, include_ticket=self.is_trusted(to_addr))
|
||||
lxm.telemetry_timebase = telemetry_timebase
|
||||
lxm.register_delivery_callback(self.outbound_telemetry_finished)
|
||||
lxm.register_failed_callback(self.outbound_telemetry_finished)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["telemetry_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
RNS.log(f"Sending telemetry update with timebase {telemetry_timebase}", RNS.LOG_DEBUG)
|
||||
|
||||
self.setpersistent(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.last_send_attempt", time.time())
|
||||
self.setstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending", True)
|
||||
self.message_router.handle_outbound(lxm)
|
||||
return "sent"
|
||||
|
||||
else:
|
||||
RNS.log(f"Telemetry update with timebase {telemetry_timebase} was already successfully sent", RNS.LOG_DEBUG)
|
||||
return "already_sent"
|
||||
else:
|
||||
return "nothing_to_send"
|
||||
|
||||
else:
|
||||
RNS.log("A telemetry update was requested, but there was nothing to send.", RNS.LOG_WARNING)
|
||||
return "nothing_to_send"
|
||||
|
||||
|
||||
def list_telemetry(self, context_dest = None, after = None, before = None, limit = None):
|
||||
@ -1447,6 +1514,7 @@ class SidebandCore():
|
||||
return []
|
||||
|
||||
def service_available(self):
|
||||
heartbeat_stale_time = 7.5
|
||||
now = time.time()
|
||||
service_heartbeat = self.getstate("service.heartbeat")
|
||||
if not service_heartbeat:
|
||||
@ -1454,9 +1522,16 @@ class SidebandCore():
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
if now - service_heartbeat > 4.0:
|
||||
RNS.log("Stale service heartbeat at "+str(now), RNS.LOG_DEBUG)
|
||||
return False
|
||||
if now - service_heartbeat > heartbeat_stale_time:
|
||||
RNS.log("Stale service heartbeat at "+str(now)+", retrying...", RNS.LOG_DEBUG)
|
||||
now = time.time()
|
||||
service_heartbeat = self.getstate("service.heartbeat")
|
||||
if now - service_heartbeat > heartbeat_stale_time:
|
||||
RNS.log("Service heartbeat did not recover after retry", RNS.LOG_DEBUG)
|
||||
return False
|
||||
else:
|
||||
RNS.log("Service heartbeat recovered at"+str(time), RNS.LOG_DEBUG)
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
except Exception as e:
|
||||
@ -1625,7 +1700,45 @@ class SidebandCore():
|
||||
RNS.log(ed, RNS.LOG_DEBUG)
|
||||
return ed
|
||||
|
||||
|
||||
def _get_destination_establishment_rate(self, destination_hash):
|
||||
try:
|
||||
mr = self.message_router
|
||||
oh = destination_hash
|
||||
ol = None
|
||||
if oh in mr.direct_links:
|
||||
ol = mr.direct_links[oh]
|
||||
elif oh in mr.backchannel_links:
|
||||
ol = mr.backchannel_links[oh]
|
||||
|
||||
if ol != None:
|
||||
ler = ol.get_establishment_rate()
|
||||
if ler:
|
||||
return ler
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
RNS.trace_exception(e)
|
||||
return None
|
||||
|
||||
def get_destination_establishment_rate(self, destination_hash):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return self._get_destination_establishment_rate(destination_hash)
|
||||
else:
|
||||
if self.is_service:
|
||||
return self._get_destination_establishment_rate(destination_hash)
|
||||
else:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
self.rpc_connection.send({"get_destination_establishment_rate": destination_hash})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
except Exception as e:
|
||||
ed = "Error while getting destination link etablishment rate over RPC: "+str(e)
|
||||
RNS.log(ed, RNS.LOG_DEBUG)
|
||||
return None
|
||||
|
||||
def __start_rpc_listener(self):
|
||||
try:
|
||||
RNS.log("Starting RPC listener", RNS.LOG_DEBUG)
|
||||
@ -1668,8 +1781,44 @@ class SidebandCore():
|
||||
connection.send(True)
|
||||
elif "get_plugins_info" in call:
|
||||
connection.send(self._get_plugins_info())
|
||||
elif "get_destination_establishment_rate" in call:
|
||||
connection.send(self._get_destination_establishment_rate(call["get_destination_establishment_rate"]))
|
||||
elif "send_message" in call:
|
||||
args = call["send_message"]
|
||||
send_result = self.send_message(
|
||||
args["content"],
|
||||
args["destination_hash"],
|
||||
args["propagation"],
|
||||
skip_fields=args["skip_fields"],
|
||||
no_display=args["no_display"],
|
||||
attachment=args["attachment"],
|
||||
image=args["image"],
|
||||
audio=args["audio"])
|
||||
connection.send(send_result)
|
||||
elif "send_command" in call:
|
||||
args = call["send_command"]
|
||||
send_result = self.send_command(
|
||||
args["content"],
|
||||
args["destination_hash"],
|
||||
args["propagation"])
|
||||
connection.send(send_result)
|
||||
elif "request_latest_telemetry" in call:
|
||||
args = call["request_latest_telemetry"]
|
||||
send_result = self.request_latest_telemetry(args["from_addr"])
|
||||
connection.send(send_result)
|
||||
elif "send_latest_telemetry" in call:
|
||||
args = call["send_latest_telemetry"]
|
||||
send_result = self.send_latest_telemetry(
|
||||
to_addr=args["to_addr"],
|
||||
stream=args["stream"],
|
||||
is_authorized_telemetry_request=args["is_authorized_telemetry_request"]
|
||||
)
|
||||
connection.send(send_result)
|
||||
elif "get_lxm_progress" in call:
|
||||
args = call["get_lxm_progress"]
|
||||
connection.send(self.get_lxm_progress(args["lxm_hash"]))
|
||||
else:
|
||||
return None
|
||||
connection.send(None)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error on client RPC connection: "+str(e), RNS.LOG_ERROR)
|
||||
@ -2320,9 +2469,11 @@ class SidebandCore():
|
||||
for entry in result:
|
||||
try:
|
||||
if not entry[2] in added_dests:
|
||||
app_data = entry[3]
|
||||
announce = {
|
||||
"dest": entry[2],
|
||||
"data": entry[3].decode("utf-8"),
|
||||
"name": LXMF.display_name_from_app_data(app_data),
|
||||
"cost": LXMF.stamp_cost_from_app_data(app_data),
|
||||
"time": entry[1],
|
||||
"type": entry[4]
|
||||
}
|
||||
@ -3906,9 +4057,34 @@ class SidebandCore():
|
||||
RNS.log("Error while creating paper message: "+str(e), RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
def _service_get_lxm_progress(self, lxm_hash):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
else:
|
||||
if self.is_client:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
|
||||
self.rpc_connection.send({"get_lxm_progress": {"lxm_hash": lxm_hash}})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while getting LXM progress over RPC: "+str(e), RNS.LOG_DEBUG)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_lxm_progress(self, lxm_hash):
|
||||
try:
|
||||
return self.message_router.get_outbound_progress(lxm_hash)
|
||||
prg = self.message_router.get_outbound_progress(lxm_hash)
|
||||
if not prg and self.is_client:
|
||||
prg = self._service_get_lxm_progress(lxm_hash)
|
||||
|
||||
return prg
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while getting message transfer progress: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
@ -3920,103 +4096,184 @@ class SidebandCore():
|
||||
RNS.log("An error occurred while getting message transfer stamp cost: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
def send_message(self, content, destination_hash, propagation, skip_fields=False, no_display=False, attachment = None, image = None, audio = None):
|
||||
try:
|
||||
if content == "":
|
||||
raise ValueError("Message content cannot be empty")
|
||||
|
||||
dest_identity = RNS.Identity.recall(destination_hash)
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if propagation:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
if skip_fields:
|
||||
fields = {}
|
||||
else:
|
||||
fields = self.get_message_fields(destination_hash)
|
||||
|
||||
if attachment != None:
|
||||
fields[LXMF.FIELD_FILE_ATTACHMENTS] = [attachment]
|
||||
if image != None:
|
||||
fields[LXMF.FIELD_IMAGE] = image
|
||||
if audio != None:
|
||||
fields[LXMF.FIELD_AUDIO] = audio
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, content, title="", desired_method=desired_method, fields = fields, include_ticket=self.is_trusted(destination_hash))
|
||||
|
||||
if not no_display:
|
||||
lxm.register_delivery_callback(self.message_notification)
|
||||
lxm.register_failed_callback(self.message_notification)
|
||||
else:
|
||||
lxm.register_delivery_callback(self.message_notification_no_display)
|
||||
lxm.register_failed_callback(self.message_notification_no_display)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["lxmf_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
self.message_router.handle_outbound(lxm)
|
||||
|
||||
if not no_display:
|
||||
self.lxm_ingest(lxm, originator=True)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
def _service_send_message(self, content, destination_hash, propagation, skip_fields=False, no_display=False, attachment = None, image = None, audio = None):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
else:
|
||||
if self.is_client:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
|
||||
self.rpc_connection.send({"send_message": {
|
||||
"content": content,
|
||||
"destination_hash": destination_hash,
|
||||
"propagation": propagation,
|
||||
"skip_fields": skip_fields,
|
||||
"no_display": no_display,
|
||||
"attachment": attachment,
|
||||
"image": image,
|
||||
"audio": audio}
|
||||
})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message over RPC: "+str(e), RNS.LOG_DEBUG)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def _service_send_command(self, content, destination_hash, propagation):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
else:
|
||||
if self.is_client:
|
||||
try:
|
||||
if self.rpc_connection == None:
|
||||
self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
|
||||
|
||||
self.rpc_connection.send({"send_command": {
|
||||
"content": content,
|
||||
"destination_hash": destination_hash,
|
||||
"propagation": propagation}
|
||||
})
|
||||
response = self.rpc_connection.recv()
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending command over RPC: "+str(e), RNS.LOG_DEBUG)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def send_message(self, content, destination_hash, propagation, skip_fields=False, no_display=False, attachment = None, image = None, audio = None):
|
||||
if self.allow_service_dispatch and self.is_client:
|
||||
try:
|
||||
return self._service_send_message(content, destination_hash, propagation, skip_fields, no_display, attachment, image, audio)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
|
||||
else:
|
||||
try:
|
||||
if content == "":
|
||||
raise ValueError("Message content cannot be empty")
|
||||
|
||||
dest_identity = RNS.Identity.recall(destination_hash)
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if propagation:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
if not self.message_router.delivery_link_available(destination_hash) and RNS.Identity.current_ratchet_id(destination_hash) != None:
|
||||
RNS.log(f"Have ratchet for {RNS.prettyhexrep(destination_hash)}, requesting opportunistic delivery of message", RNS.LOG_DEBUG)
|
||||
desired_method = LXMF.LXMessage.OPPORTUNISTIC
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
if skip_fields:
|
||||
fields = {}
|
||||
else:
|
||||
fields = self.get_message_fields(destination_hash)
|
||||
|
||||
if attachment != None:
|
||||
fields[LXMF.FIELD_FILE_ATTACHMENTS] = [attachment]
|
||||
if image != None:
|
||||
fields[LXMF.FIELD_IMAGE] = image
|
||||
if audio != None:
|
||||
fields[LXMF.FIELD_AUDIO] = audio
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, content, title="", desired_method=desired_method, fields = fields, include_ticket=self.is_trusted(destination_hash))
|
||||
|
||||
if not no_display:
|
||||
lxm.register_delivery_callback(self.message_notification)
|
||||
lxm.register_failed_callback(self.message_notification)
|
||||
else:
|
||||
lxm.register_delivery_callback(self.message_notification_no_display)
|
||||
lxm.register_failed_callback(self.message_notification_no_display)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["lxmf_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
self.message_router.handle_outbound(lxm)
|
||||
|
||||
if not no_display:
|
||||
self.lxm_ingest(lxm, originator=True)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
|
||||
def send_command(self, content, destination_hash, propagation):
|
||||
try:
|
||||
if content == "":
|
||||
if self.allow_service_dispatch and self.is_client:
|
||||
try:
|
||||
return self._service_send_command(content, destination_hash, propagation)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
|
||||
commands = []
|
||||
if content.startswith("echo "):
|
||||
echo_content = content.replace("echo ", "").encode("utf-8")
|
||||
if len(echo_content) > 0:
|
||||
commands.append({Commands.ECHO: echo_content})
|
||||
elif content.startswith("sig"):
|
||||
commands.append({Commands.SIGNAL_REPORT: True})
|
||||
elif content.startswith("ping"):
|
||||
commands.append({Commands.PING: True})
|
||||
else:
|
||||
commands.append({Commands.PLUGIN_COMMAND: content})
|
||||
else:
|
||||
try:
|
||||
if content == "":
|
||||
return False
|
||||
|
||||
if len(commands) == 0:
|
||||
commands = []
|
||||
if content.startswith("echo "):
|
||||
echo_content = content.replace("echo ", "").encode("utf-8")
|
||||
if len(echo_content) > 0:
|
||||
commands.append({Commands.ECHO: echo_content})
|
||||
elif content.startswith("sig"):
|
||||
commands.append({Commands.SIGNAL_REPORT: True})
|
||||
elif content.startswith("ping"):
|
||||
commands.append({Commands.PING: True})
|
||||
else:
|
||||
commands.append({Commands.PLUGIN_COMMAND: content})
|
||||
|
||||
if len(commands) == 0:
|
||||
return False
|
||||
|
||||
dest_identity = RNS.Identity.recall(destination_hash)
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if propagation:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
if not self.message_router.delivery_link_available(destination_hash) and RNS.Identity.current_ratchet_id(destination_hash) != None:
|
||||
RNS.log(f"Have ratchet for {RNS.prettyhexrep(destination_hash)}, requesting opportunistic delivery of command", RNS.LOG_DEBUG)
|
||||
desired_method = LXMF.LXMessage.OPPORTUNISTIC
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, "", title="", desired_method=desired_method, fields = {LXMF.FIELD_COMMANDS: commands}, include_ticket=self.is_trusted(destination_hash))
|
||||
lxm.register_delivery_callback(self.message_notification)
|
||||
lxm.register_failed_callback(self.message_notification)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["lxmf_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
self.message_router.handle_outbound(lxm)
|
||||
self.lxm_ingest(lxm, originator=True)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
dest_identity = RNS.Identity.recall(destination_hash)
|
||||
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||
source = self.lxmf_destination
|
||||
|
||||
if propagation:
|
||||
desired_method = LXMF.LXMessage.PROPAGATED
|
||||
else:
|
||||
desired_method = LXMF.LXMessage.DIRECT
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, "", title="", desired_method=desired_method, fields = {LXMF.FIELD_COMMANDS: commands}, include_ticket=self.is_trusted(destination_hash))
|
||||
lxm.register_delivery_callback(self.message_notification)
|
||||
lxm.register_failed_callback(self.message_notification)
|
||||
|
||||
if self.message_router.get_outbound_propagation_node() != None:
|
||||
if self.config["lxmf_try_propagation_on_fail"]:
|
||||
lxm.try_propagation_on_fail = True
|
||||
|
||||
self.message_router.handle_outbound(lxm)
|
||||
self.lxm_ingest(lxm, originator=True)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
def new_conversation(self, dest_str, name = "", trusted = False):
|
||||
if len(dest_str) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2:
|
||||
return False
|
||||
|
@ -89,7 +89,8 @@ class Announces():
|
||||
for announce in self.announces:
|
||||
context_dest = announce["dest"]
|
||||
ts = announce["time"]
|
||||
a_data = announce["data"]
|
||||
a_name = announce["name"]
|
||||
a_cost = announce["cost"]
|
||||
dest_type = announce["type"]
|
||||
|
||||
if not context_dest in self.added_item_dests:
|
||||
@ -98,15 +99,16 @@ class Announces():
|
||||
else:
|
||||
trust_icon = "account-question"
|
||||
|
||||
def gen_info(ts, dest, name, dtype):
|
||||
def gen_info(ts, dest, name, cost, dtype):
|
||||
name = multilingual_markup(escape_markup(str(name)).encode("utf-8")).decode("utf-8")
|
||||
cost = str(cost)
|
||||
def x(sender):
|
||||
yes_button = MDRectangleFlatButton(text="OK",font_size=dp(18))
|
||||
if dtype == "lxmf.delivery":
|
||||
ad_text = "[size=22dp]LXMF Peer[/size]\n\nReceived: "+ts+"\nAnnounced Name: "+name+"\nAddress: "+RNS.prettyhexrep(dest)
|
||||
ad_text = "[size=22dp]LXMF Peer[/size]\n\n[b]Received[/b] "+ts+"\n[b]Address[/b] "+RNS.prettyhexrep(dest)+"\n[b]Name[/b] "+name+"\n[b]Stamp Cost[/b] "+cost
|
||||
|
||||
if dtype == "lxmf.propagation":
|
||||
ad_text = "[size=22dp]LXMF Propagation Node[/size]\n\nReceived: "+ts+"\nAddress: "+RNS.prettyhexrep(dest)
|
||||
ad_text = "[size=22dp]LXMF Propagation Node[/size]\n\n[b]Received[/b] "+ts+"\n[b]Address[/b] "+RNS.prettyhexrep(dest)
|
||||
|
||||
dialog = MDDialog(
|
||||
text=ad_text,
|
||||
@ -135,7 +137,7 @@ class Announces():
|
||||
disp_name = "Unknown Announce"
|
||||
iconl = IconLeftWidget(icon="progress-question")
|
||||
|
||||
item = TwoLineAvatarIconListItem(text=time_string, secondary_text=disp_name, on_release=gen_info(time_string, context_dest, a_data, dest_type))
|
||||
item = TwoLineAvatarIconListItem(text=time_string, secondary_text=disp_name, on_release=gen_info(time_string, context_dest, a_name, a_cost, dest_type))
|
||||
item.add_widget(iconl)
|
||||
item.sb_uid = context_dest
|
||||
item.ts = ts
|
||||
|
@ -616,7 +616,7 @@ MDScreen:
|
||||
MDLabel:
|
||||
text: "Shared Instance Access\\n"
|
||||
id: connectivity_shared_access_label
|
||||
font_style: "H6"
|
||||
font_style: "H5"
|
||||
|
||||
MDLabel:
|
||||
id: connectivity_shared_access
|
||||
@ -1176,7 +1176,7 @@ MDScreen:
|
||||
padding: [0,dp(0),dp(0),dp(0)]
|
||||
text: "Plugin Settings"
|
||||
id: plugins_active_heading
|
||||
font_style: "H6"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
@ -1286,7 +1286,7 @@ MDScreen:
|
||||
|
||||
MDLabel:
|
||||
text: "User Options"
|
||||
font_style: "H6"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
@ -1326,8 +1326,17 @@ MDScreen:
|
||||
height: dp(64)
|
||||
|
||||
MDLabel:
|
||||
text: "Address & Identity"
|
||||
text: "•"
|
||||
font_style: "H6"
|
||||
text_size: self.size
|
||||
halign: "center"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
padding: [0, dp(2), 0, dp(22)]
|
||||
|
||||
MDLabel:
|
||||
text: "Address & Identity"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
@ -1361,8 +1370,17 @@ MDScreen:
|
||||
height: dp(64)
|
||||
|
||||
MDLabel:
|
||||
text: "Appearance"
|
||||
text: "•"
|
||||
font_style: "H6"
|
||||
text_size: self.size
|
||||
halign: "center"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
padding: [0, dp(2), 0, dp(22)]
|
||||
|
||||
MDLabel:
|
||||
text: "Appearance"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
@ -1464,8 +1482,17 @@ MDScreen:
|
||||
active: False
|
||||
|
||||
MDLabel:
|
||||
text: "\\nBehaviour"
|
||||
text: "•"
|
||||
font_style: "H6"
|
||||
text_size: self.size
|
||||
halign: "center"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
padding: [0, dp(22), 0, dp(2)]
|
||||
|
||||
MDLabel:
|
||||
text: "\\nBehaviour"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
@ -1498,7 +1525,7 @@ MDScreen:
|
||||
height: dp(48)
|
||||
|
||||
MDLabel:
|
||||
text: "Try Propagation Node on direct delivery failure"
|
||||
text: "Try propagation on direct delivery failure"
|
||||
font_style: "H6"
|
||||
|
||||
MDSwitch:
|
||||
@ -1684,8 +1711,17 @@ MDScreen:
|
||||
active: False
|
||||
|
||||
MDLabel:
|
||||
text: "Input Options & Localisation"
|
||||
text: "•"
|
||||
font_style: "H6"
|
||||
text_size: self.size
|
||||
halign: "center"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
padding: [0, dp(0), 0, dp(30)]
|
||||
|
||||
MDLabel:
|
||||
text: "Input Options & Localisation"
|
||||
font_style: "H5"
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
|
||||
|
@ -102,7 +102,6 @@ class Messages():
|
||||
self.list.remove_widget(self.load_more_button)
|
||||
|
||||
def message_details_dialog(self, lxm_hash):
|
||||
RNS.log(f"Opening dialog for {RNS.prettyhexrep(lxm_hash)}", RNS.LOG_DEBUG)
|
||||
ss = int(dp(16))
|
||||
ms = int(dp(14))
|
||||
|
||||
@ -247,88 +246,100 @@ class Messages():
|
||||
else:
|
||||
w.line_color = (1.0, 1.0, 1.0, 0.5)
|
||||
|
||||
if m["state"] == LXMF.LXMessage.SENDING or m["state"] == LXMF.LXMessage.OUTBOUND:
|
||||
if m["state"] == LXMF.LXMessage.SENDING or m["state"] == LXMF.LXMessage.OUTBOUND or m["state"] == LXMF.LXMessage.SENT:
|
||||
msg = self.app.sideband.message(m["hash"])
|
||||
|
||||
if msg["state"] == LXMF.LXMessage.OUTBOUND or msg["state"] == LXMF.LXMessage.SENDING:
|
||||
w.md_bg_color = msg_color = mdc(color_unknown, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
prgstr = ""
|
||||
sphrase = "Sending"
|
||||
prg = self.app.sideband.get_lxm_progress(msg["hash"])
|
||||
if prg != None:
|
||||
prgstr = ", "+str(round(prg*100, 1))+"% done"
|
||||
if prg <= 0.00:
|
||||
stamp_cost = self.app.sideband.get_lxm_stamp_cost(msg["hash"])
|
||||
if stamp_cost:
|
||||
sphrase = f"Generating stamp with cost {stamp_cost}"
|
||||
prgstr = ""
|
||||
else:
|
||||
if msg != None:
|
||||
delivery_syms = ""
|
||||
# if msg["extras"] != None and "ratchet_id" in m["extras"]:
|
||||
# delivery_syms += " ⚙️"
|
||||
if msg["method"] == LXMF.LXMessage.OPPORTUNISTIC:
|
||||
delivery_syms += " 📨"
|
||||
if msg["method"] == LXMF.LXMessage.DIRECT:
|
||||
delivery_syms += " 🔗"
|
||||
if msg["method"] == LXMF.LXMessage.PROPAGATED:
|
||||
delivery_syms += " 📦"
|
||||
delivery_syms = multilingual_markup(delivery_syms.encode("utf-8")).decode("utf-8")
|
||||
|
||||
if msg["state"] == LXMF.LXMessage.OUTBOUND or msg["state"] == LXMF.LXMessage.SENDING or msg["state"] == LXMF.LXMessage.SENT:
|
||||
w.md_bg_color = msg_color = mdc(color_unknown, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
prgstr = ""
|
||||
sphrase = "Sending"
|
||||
prg = self.app.sideband.get_lxm_progress(msg["hash"])
|
||||
if prg != None:
|
||||
prgstr = ", "+str(round(prg*100, 1))+"% done"
|
||||
if prg <= 0.00:
|
||||
stamp_cost = self.app.sideband.get_lxm_stamp_cost(msg["hash"])
|
||||
if stamp_cost:
|
||||
sphrase = f"Generating stamp with cost {stamp_cost}"
|
||||
prgstr = ""
|
||||
else:
|
||||
sphrase = "Waiting for path"
|
||||
elif prg <= 0.01:
|
||||
sphrase = "Waiting for path"
|
||||
elif prg <= 0.01:
|
||||
sphrase = "Waiting for path"
|
||||
elif prg <= 0.03:
|
||||
sphrase = "Establishing link"
|
||||
elif prg <= 0.05:
|
||||
sphrase = "Link established"
|
||||
elif prg >= 0.05:
|
||||
sphrase = "Sending"
|
||||
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] "+sphrase+prgstr+" "
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
elif prg <= 0.03:
|
||||
sphrase = "Establishing link"
|
||||
elif prg <= 0.05:
|
||||
sphrase = "Link established"
|
||||
elif prg >= 0.05:
|
||||
sphrase = "Sending"
|
||||
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] "+sphrase+prgstr+" "
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
|
||||
|
||||
if msg["state"] == LXMF.LXMessage.DELIVERED:
|
||||
w.md_bg_color = msg_color = mdc(color_delivered, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Delivered"
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
if msg["state"] == LXMF.LXMessage.DELIVERED:
|
||||
w.md_bg_color = msg_color = mdc(color_delivered, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+delivery_syms+"\n[b]State[/b] Delivered"
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
|
||||
if msg["method"] == LXMF.LXMessage.PAPER:
|
||||
w.md_bg_color = msg_color = mdc(color_paper, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Paper Message"
|
||||
m["state"] = msg["state"]
|
||||
if msg["method"] == LXMF.LXMessage.PAPER:
|
||||
w.md_bg_color = msg_color = mdc(color_paper, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Paper Message"
|
||||
m["state"] = msg["state"]
|
||||
|
||||
if msg["method"] == LXMF.LXMessage.PROPAGATED and msg["state"] == LXMF.LXMessage.SENT:
|
||||
w.md_bg_color = msg_color = mdc(color_propagated, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] On Propagation Net"
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
if msg["method"] == LXMF.LXMessage.PROPAGATED and msg["state"] == LXMF.LXMessage.SENT:
|
||||
w.md_bg_color = msg_color = mdc(color_propagated, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+delivery_syms+"\n[b]State[/b] On Propagation Net"
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
m["state"] = msg["state"]
|
||||
|
||||
if msg["state"] == LXMF.LXMessage.FAILED:
|
||||
w.md_bg_color = msg_color = mdc(color_failed, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Failed"
|
||||
m["state"] = msg["state"]
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
w.dmenu.items.append(w.dmenu.retry_item)
|
||||
if msg["state"] == LXMF.LXMessage.FAILED:
|
||||
w.md_bg_color = msg_color = mdc(color_failed, intensity_msgs)
|
||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||
titlestr = ""
|
||||
if msg["title"]:
|
||||
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Failed"
|
||||
m["state"] = msg["state"]
|
||||
if w.has_audio:
|
||||
alstr = RNS.prettysize(w.audio_size)
|
||||
w.heading += f"\n[b]Audio Message[/b] ({alstr})"
|
||||
w.dmenu.items.append(w.dmenu.retry_item)
|
||||
|
||||
|
||||
def hide_widget(self, wid, dohide=True):
|
||||
@ -357,10 +368,14 @@ class Messages():
|
||||
|
||||
for m in self.new_messages:
|
||||
if not m["hash"] in self.added_item_hashes:
|
||||
if not self.is_trusted:
|
||||
message_input = str( escape_markup(m["content"].decode("utf-8")) ).encode("utf-8")
|
||||
else:
|
||||
message_input = m["content"]
|
||||
try:
|
||||
if not self.is_trusted:
|
||||
message_input = str( escape_markup(m["content"].decode("utf-8")) ).encode("utf-8")
|
||||
else:
|
||||
message_input = m["content"]
|
||||
except Exception as e:
|
||||
RNS.log(f"Message content could not be decoded: {e}", RNS.LOG_DEBUG)
|
||||
message_input = b""
|
||||
|
||||
if message_input.strip() == b"":
|
||||
if not ("lxm" in m and m["lxm"] != None and m["lxm"].fields != None and LXMF.FIELD_COMMANDS in m["lxm"].fields):
|
||||
@ -385,6 +400,17 @@ class Messages():
|
||||
stamp_valid = False
|
||||
stamp_value = None
|
||||
|
||||
delivery_syms = ""
|
||||
# if m["extras"] != None and "ratchet_id" in m["extras"]:
|
||||
# delivery_syms += " ⚙️"
|
||||
if m["method"] == LXMF.LXMessage.OPPORTUNISTIC:
|
||||
delivery_syms += " 📨"
|
||||
if m["method"] == LXMF.LXMessage.DIRECT:
|
||||
delivery_syms += " 🔗"
|
||||
if m["method"] == LXMF.LXMessage.PROPAGATED:
|
||||
delivery_syms += " 📦"
|
||||
delivery_syms = multilingual_markup(delivery_syms.encode("utf-8")).decode("utf-8")
|
||||
|
||||
if "lxm" in m and m["lxm"] != None and m["lxm"].signature_validated:
|
||||
signature_valid = True
|
||||
|
||||
@ -490,11 +516,11 @@ class Messages():
|
||||
if m["source"] == self.app.sideband.lxmf_destination.hash:
|
||||
if m["state"] == LXMF.LXMessage.DELIVERED:
|
||||
msg_color = mdc(color_delivered, intensity_msgs)
|
||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Delivered"
|
||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+delivery_syms+"\n[b]State[/b] Delivered"
|
||||
|
||||
elif m["method"] == LXMF.LXMessage.PROPAGATED and m["state"] == LXMF.LXMessage.SENT:
|
||||
msg_color = mdc(color_propagated, intensity_msgs)
|
||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] On Propagation Net"
|
||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+delivery_syms+"\n[b]State[/b] On Propagation Net"
|
||||
|
||||
elif m["method"] == LXMF.LXMessage.PAPER:
|
||||
msg_color = mdc(color_paper, intensity_msgs)
|
||||
@ -521,7 +547,7 @@ class Messages():
|
||||
# if stamp_valid:
|
||||
# txstr += f" [b]Stamp[/b] value is {stamp_value} "
|
||||
|
||||
heading_str += "[b]Sent[/b] "+txstr
|
||||
heading_str += "[b]Sent[/b] "+txstr+delivery_syms
|
||||
heading_str += "\n[b]Received[/b] "+rxstr
|
||||
|
||||
if rcvd_d_str != "":
|
||||
@ -966,7 +992,7 @@ class Messages():
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": "Copy message text",
|
||||
"height": dp(40),
|
||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||
"on_release": gen_copy(message_input.decode("utf-8"), item)
|
||||
},
|
||||
{
|
||||
"text": "Delete",
|
||||
@ -1000,7 +1026,7 @@ class Messages():
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": "Copy message text",
|
||||
"height": dp(40),
|
||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||
"on_release": gen_copy(message_input.decode("utf-8"), item)
|
||||
},
|
||||
{
|
||||
"text": "Delete",
|
||||
@ -1018,7 +1044,7 @@ class Messages():
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": "Copy",
|
||||
"height": dp(40),
|
||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||
"on_release": gen_copy(message_input.decode("utf-8"), item)
|
||||
},
|
||||
{
|
||||
"text": "Delete",
|
||||
@ -1035,7 +1061,7 @@ class Messages():
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": "Copy",
|
||||
"height": dp(40),
|
||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||
"on_release": gen_copy(message_input.decode("utf-8"), item)
|
||||
},
|
||||
{
|
||||
"viewclass": "OneLineListItem",
|
||||
@ -1058,7 +1084,7 @@ class Messages():
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": "Copy",
|
||||
"height": dp(40),
|
||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||
"on_release": gen_copy(message_input.decode("utf-8"), item)
|
||||
},
|
||||
{
|
||||
"text": "Delete",
|
||||
|
@ -796,14 +796,10 @@ class RVDetails(MDRecycleView):
|
||||
self.entries.append({"icon": "routes", "text": f"Current path on [b]{nhi}[/b]", "on_release": pass_job})
|
||||
|
||||
try:
|
||||
mr = self.delegate.app.sideband.message_router
|
||||
oh = self.delegate.object_hash
|
||||
if oh in mr.direct_links:
|
||||
ol = mr.direct_links[oh]
|
||||
ler = ol.get_establishment_rate()
|
||||
if ler:
|
||||
lers = RNS.prettyspeed(ler, "b")
|
||||
self.entries.append({"icon": "lock-check-outline", "text": f"Direct link established, LER is [b]{lers}[/b]", "on_release": pass_job})
|
||||
ler = self.delegate.app.sideband.get_destination_establishment_rate(self.delegate.object_hash)
|
||||
if ler:
|
||||
lers = RNS.prettyspeed(ler, "b")
|
||||
self.entries.append({"icon": "lock-check-outline", "text": f"Direct link established, LER is [b]{lers}[/b]", "on_release": pass_job})
|
||||
except Exception as e:
|
||||
RNS.trace_exception(e)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user