Added command handling and sending

This commit is contained in:
Mark Qvist 2023-10-30 02:28:35 +01:00
parent c1b5b776d7
commit 7076fae5cc
4 changed files with 172 additions and 25 deletions

View File

@ -829,7 +829,9 @@ class SidebandApp(MDApp):
self.message_send_action()
if text == "l":
if self.root.ids.screen_manager.current == "map_screen":
if self.root.ids.screen_manager.current == "messages_screen":
self.message_propagation_action(self)
elif self.root.ids.screen_manager.current == "map_screen":
self.map_layers_action()
else:
self.announces_action(self)
@ -1006,6 +1008,7 @@ class SidebandApp(MDApp):
def open_conversation(self, context_dest, direction="left"):
self.outbound_mode_paper = False
self.outbound_mode_command = False
if self.sideband.config["propagation_by_default"]:
self.outbound_mode_propagation = True
else:
@ -1075,7 +1078,26 @@ class SidebandApp(MDApp):
else:
msg_content = self.messages_view.ids.message_text.text
context_dest = self.messages_view.ids.messages_scrollview.active_conversation
if self.outbound_mode_paper:
if self.outbound_mode_command:
if self.sideband.send_command(msg_content, context_dest, False):
self.messages_view.ids.message_text.text = ""
self.messages_view.ids.messages_scrollview.scroll_y = 0
self.jobs(0)
else:
self.messages_view.send_error_dialog = MDDialog(
title="Error",
text="Could not send the command. Check that the syntax is correct, and that the command is supported.",
buttons=[
MDRectangleFlatButton(
text="OK",
font_size=dp(18),
on_release=self.messages_view.close_send_error_dialog
)
],
)
self.messages_view.send_error_dialog.open()
elif self.outbound_mode_paper:
if self.sideband.paper_message(msg_content, context_dest):
self.messages_view.ids.message_text.text = ""
self.messages_view.ids.messages_scrollview.scroll_y = 0
@ -1117,16 +1139,24 @@ class SidebandApp(MDApp):
self.object_details_action(self.messages_view, from_conv=True)
def message_propagation_action(self, sender):
if self.outbound_mode_command:
self.outbound_mode_paper = False
self.outbound_mode_propagation = False
self.outbound_mode_command = False
else:
if self.outbound_mode_paper:
self.outbound_mode_paper = False
self.outbound_mode_propagation = False
self.outbound_mode_command = True
else:
if self.outbound_mode_propagation:
self.outbound_mode_paper = True
self.outbound_mode_propagation = False
self.outbound_mode_command = False
else:
self.outbound_mode_propagation = True
self.outbound_mode_paper = False
self.outbound_mode_command = False
self.update_message_widgets()
@ -1137,6 +1167,10 @@ class SidebandApp(MDApp):
if self.outbound_mode_paper:
mode_item.icon = "qrcode"
self.messages_view.ids.message_text.hint_text = "Paper message"
else:
if self.outbound_mode_command:
mode_item.icon = "console"
self.messages_view.ids.message_text.hint_text = "Send command or request"
else:
if not self.outbound_mode_propagation:
mode_item.icon = "lan-connect"

View File

@ -1976,7 +1976,7 @@ class SidebandCore():
messages = messages[-limit:]
return messages
def _db_save_lxm(self, lxm, context_dest, originator = False):
def _db_save_lxm(self, lxm, context_dest, originator = False, own_command = False):
state = lxm.state
packed_telemetry = None
@ -1998,7 +1998,7 @@ class SidebandCore():
# TODO: Implement
pass
if len(lxm.content) != 0 or len(lxm.title) != 0:
if own_command or len(lxm.content) != 0 or len(lxm.title) != 0:
db = self.__db_connect()
dbc = db.cursor()
@ -2977,7 +2977,7 @@ class SidebandCore():
RNS.log("Error while creating paper message: "+str(e), RNS.LOG_ERROR)
return False
def send_message(self, content, destination_hash, propagation):
def send_message(self, content, destination_hash, propagation, skip_fields=False):
try:
if content == "":
raise ValueError("Message content cannot be empty")
@ -2991,7 +2991,54 @@ class SidebandCore():
else:
desired_method = LXMF.LXMessage.DIRECT
lxm = LXMF.LXMessage(dest, source, content, title="", desired_method=desired_method, fields = self.get_message_fields(destination_hash))
if skip_fields:
fields = {}
else:
fields = self.get_message_fields(destination_hash)
lxm = LXMF.LXMessage(dest, source, content, title="", desired_method=desired_method, fields = fields)
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 send_command(self, content, destination_hash, propagation):
try:
if content == "":
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})
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:
desired_method = LXMF.LXMessage.DIRECT
lxm = LXMF.LXMessage(dest, source, "", title="", desired_method=desired_method, fields = {LXMF.FIELD_COMMANDS: commands})
lxm.register_delivery_callback(self.message_notification)
lxm.register_failed_callback(self.message_notification)
@ -3060,6 +3107,7 @@ class SidebandCore():
should_notify = False
is_trusted = False
telemetry_only = False
own_command = False
unread_reason_tx = False
if originator:
@ -3069,18 +3117,21 @@ class SidebandCore():
context_dest = message.source_hash
is_trusted = self.is_trusted(context_dest)
if originator and LXMF.FIELD_COMMANDS in message.fields:
own_command = True
if self._db_message(message.hash):
RNS.log("Message exists, setting state to: "+str(message.state), RNS.LOG_DEBUG)
self._db_message_set_state(message.hash, message.state)
else:
RNS.log("Message does not exist, saving", RNS.LOG_DEBUG)
self._db_save_lxm(message, context_dest, originator)
self._db_save_lxm(message, context_dest, originator, own_command=own_command)
if is_trusted:
should_notify = True
if len(message.content) == 0 and len(message.title) == 0:
if (LXMF.FIELD_TELEMETRY in message.fields or LXMF.FIELD_TELEMETRY_STREAM in message.fields):
if (LXMF.FIELD_TELEMETRY in message.fields or LXMF.FIELD_TELEMETRY_STREAM in message.fields or LXMF.FIELD_COMMANDS in message.fields):
RNS.log("Squelching notification due to telemetry-only message", RNS.LOG_DEBUG)
telemetry_only = True
@ -3236,16 +3287,57 @@ class SidebandCore():
RNS.log("LXMF delivery "+str(time_string)+". "+str(signature_string)+".", RNS.LOG_DEBUG)
try:
if self.config["lxmf_ignore_unknown"] == True:
context_dest = message.source_hash
if self.config["lxmf_ignore_unknown"] == True:
if self._db_conversation(context_dest) == None:
RNS.log("Dropping message from unknown sender "+RNS.prettyhexrep(context_dest), RNS.LOG_DEBUG)
return
if message.signature_validated and LXMF.FIELD_COMMANDS in message.fields:
if self.requests_allowed_from(context_dest):
commands = message.fields[LXMF.FIELD_COMMANDS]
self.handle_commands(commands, message)
else:
self.lxm_ingest(message)
except Exception as e:
RNS.log("Error while ingesting LXMF message "+RNS.prettyhexrep(message.hash)+" to database: "+str(e))
def handle_commands(self, commands, message):
try:
context_dest = message.source_hash
RNS.log("Handling commands from "+str(context_dest), RNS.LOG_DEBUG)
for command in commands:
if Commands.TELEMETRY_REQUEST in command:
timebase = int(command[Commands.TELEMETRY_REQUEST])
RNS.log("Handling telemetry request with timebase "+str(timebase), RNS.LOG_DEBUG)
elif Commands.ECHO in command:
msg_content = "Echo reply: "+command[Commands.ECHO].decode("utf-8")
RNS.log("Handling echo request", RNS.LOG_DEBUG)
self.send_message(msg_content, context_dest, False, skip_fields=True)
elif Commands.SIGNAL_REPORT in command:
RNS.log("Handling signal report", RNS.LOG_DEBUG)
phy_str = ""
if message.q != None:
phy_str += f"Link Quality: {message.q}%\n"
if message.rssi != None:
phy_str += f"RSSI: {message.rssi} dBm\n"
if message.snr != None:
phy_str += f"SNR: {message.rssi} dB\n"
if len(phy_str) != 0:
phy_str = phy_str[:-1]
else:
phy_str = "No reception info available"
self.send_message(phy_str, context_dest, False, skip_fields=True)
except Exception as e:
RNS.log("Error while handling commands: "+str(e), RNS.LOG_ERROR)
def get_display_name_bytes(self):
return self.config["display_name"].encode("utf-8")

View File

@ -10,6 +10,8 @@ from .geo import azalt, angle_to_horizon, radio_horizon, shared_radio_horizon
class Commands():
TELEMETRY_REQUEST = 0x01
ECHO = 0x02
SIGNAL_REPORT = 0x03
class Telemeter():
@staticmethod

View File

@ -22,11 +22,11 @@ import subprocess
import shlex
if RNS.vendor.platformutils.get_platform() == "android":
from sideband.sense import Telemeter
from sideband.sense import Telemeter, Commands
from ui.helpers import ts_format, file_ts_format, mdc
from ui.helpers import color_received, color_delivered, color_propagated, color_paper, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
else:
from sbapp.sideband.sense import Telemeter
from sbapp.sideband.sense import Telemeter, Commands
from .helpers import ts_format, file_ts_format, mdc
from .helpers import color_received, color_delivered, color_propagated, color_paper, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
@ -178,14 +178,29 @@ class Messages():
txstr = time.strftime(ts_format, time.localtime(m["sent"]))
rxstr = time.strftime(ts_format, time.localtime(m["received"]))
titlestr = ""
extra_content = ""
extra_telemetry = {}
telemeter = None
signature_valid = False
if "lxm" in m and m["lxm"] != None and m["lxm"].signature_validated:
signature_valid = True
if "extras" in m and m["extras"] != None and "packed_telemetry" in m["extras"]:
try:
telemeter = Telemeter.from_packed(m["extras"]["packed_telemetry"])
except Exception as e:
pass
if "lxm" in m and m["lxm"] != None and m["lxm"].fields != None and LXMF.FIELD_COMMANDS in m["lxm"].fields:
commands = m["lxm"].fields[LXMF.FIELD_COMMANDS]
for command in commands:
if Commands.ECHO in command:
extra_content = "[font=RobotoMono-Regular]> echo "+command[Commands.ECHO].decode("utf-8")+"[/font]\n"
if Commands.SIGNAL_REPORT in command:
extra_content = "[font=RobotoMono-Regular]> sig[/font]\n"
extra_content = extra_content[:-1]
if telemeter == None and "lxm" in m and m["lxm"] and m["lxm"].fields != None and LXMF.FIELD_TELEMETRY in m["lxm"].fields:
try:
packed_telemetry = m["lxm"].fields[LXMF.FIELD_TELEMETRY]
@ -272,8 +287,12 @@ class Messages():
if rcvd_d_str != "":
heading_str += rcvd_d_str
pre_content = ""
if not signature_valid:
pre_content += "[b]Warning![/b] The signature for this message could not be validated. Check that you have received an announce from this sender. If you already have, or other messages from the sender do not display this warning, [b]this message is likely to be fake[/b].\n\n"
item = ListLXMessageCard(
text=m["content"].decode("utf-8"),
text=pre_content+m["content"].decode("utf-8")+extra_content,
heading=heading_str,
md_bg_color=msg_color,
)