mirror of
https://github.com/markqvist/LXMF-Tools.git
synced 2025-05-19 14:30:40 -04:00
Merge branch 'SebastianObi:main' into main
This commit is contained in:
commit
a36d8f9a9d
25 changed files with 2747 additions and 444 deletions
4
lxmf_ping/Examples/README.md
Normal file
4
lxmf_ping/Examples/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Examples
|
||||
This folder contains sample configurations for different application environments or scenarios.
|
||||
|
||||
Copy these files accordingly to the appropriate configuration folder. Then customize the content of these files to your needs.
|
|
@ -44,6 +44,9 @@ import pickle
|
|||
#### String ####
|
||||
import string
|
||||
|
||||
#### Regex ####
|
||||
import re
|
||||
|
||||
#### Other ####
|
||||
import random
|
||||
import secrets
|
||||
|
@ -90,9 +93,10 @@ class lxmf_connection:
|
|||
message_notification_callback = None
|
||||
message_notification_success_callback = None
|
||||
message_notification_failed_callback = None
|
||||
config_set_callback = None
|
||||
|
||||
|
||||
def __init__(self, storage_path=None, identity_file="identity", identity=None, destination_name="lxmf", destination_type="delivery", display_name="", announce_data=None, send_delay=0, desired_method="direct", propagation_node=None, try_propagation_on_fail=False, announce_startup=False, announce_startup_delay=0, announce_periodic=False, announce_periodic_interval=360, sync_startup=False, sync_startup_delay=0, sync_limit=8, sync_periodic=False, sync_periodic_interval=360):
|
||||
def __init__(self, storage_path=None, identity_file="identity", identity=None, destination_name="lxmf", destination_type="delivery", display_name="", announce_data=None, announce_hidden=False, send_delay=0, desired_method="direct", propagation_node=None, propagation_node_auto=False, propagation_node_active=None, try_propagation_on_fail=False, announce_startup=False, announce_startup_delay=0, announce_periodic=False, announce_periodic_interval=360, sync_startup=False, sync_startup_delay=0, sync_limit=8, sync_periodic=False, sync_periodic_interval=360):
|
||||
self.storage_path = storage_path
|
||||
|
||||
self.identity_file = identity_file
|
||||
|
@ -105,6 +109,7 @@ class lxmf_connection:
|
|||
|
||||
self.display_name = display_name
|
||||
self.announce_data = announce_data
|
||||
self.announce_hidden = announce_hidden
|
||||
|
||||
self.send_delay = int(send_delay)
|
||||
|
||||
|
@ -113,6 +118,8 @@ class lxmf_connection:
|
|||
else:
|
||||
self.desired_method_direct = True
|
||||
self.propagation_node = propagation_node
|
||||
self.propagation_node_auto = propagation_node_auto
|
||||
self.propagation_node_active = propagation_node_active
|
||||
self.try_propagation_on_fail = try_propagation_on_fail
|
||||
|
||||
self.announce_startup = announce_startup
|
||||
|
@ -127,6 +134,10 @@ class lxmf_connection:
|
|||
self.sync_periodic = sync_periodic
|
||||
self.sync_periodic_interval = int(sync_periodic_interval)
|
||||
|
||||
if not self.storage_path:
|
||||
log("LXMF - No storage_path parameter", LOG_ERROR)
|
||||
return
|
||||
|
||||
if not os.path.isdir(self.storage_path):
|
||||
os.makedirs(self.storage_path)
|
||||
log("LXMF - Storage path was created", LOG_NOTICE)
|
||||
|
@ -181,10 +192,18 @@ class lxmf_connection:
|
|||
|
||||
self.destination.set_link_established_callback(self.client_connected)
|
||||
|
||||
self.autoselect_propagation_node()
|
||||
if self.propagation_node_auto:
|
||||
self.propagation_callback = lxmf_connection_propagation(self, "lxmf.propagation")
|
||||
RNS.Transport.register_announce_handler(self.propagation_callback)
|
||||
if self.propagation_node_active:
|
||||
self.propagation_node_set(self.propagation_node_active)
|
||||
elif self.propagation_node:
|
||||
self.propagation_node_set(self.propagation_node)
|
||||
else:
|
||||
self.propagation_node_set(self.propagation_node)
|
||||
|
||||
if self.announce_startup or self.announce_periodic:
|
||||
self.announce(True)
|
||||
self.announce(initial=True)
|
||||
|
||||
if self.sync_startup or self.sync_periodic:
|
||||
self.sync(True)
|
||||
|
@ -211,6 +230,10 @@ class lxmf_connection:
|
|||
self.message_notification_failed_callback = handler_function
|
||||
|
||||
|
||||
def register_config_set_callback(self, handler_function):
|
||||
self.config_set_callback = handler_function
|
||||
|
||||
|
||||
def destination_hash(self):
|
||||
return self.destination.hash
|
||||
|
||||
|
@ -254,7 +277,7 @@ class lxmf_connection:
|
|||
return ""
|
||||
|
||||
|
||||
def send(self, destination, content="", title="", fields=None, timestamp=None, app_data=""):
|
||||
def send(self, destination, content="", title="", fields=None, timestamp=None, app_data="", destination_name=None, destination_type=None):
|
||||
if type(destination) is not bytes:
|
||||
if len(destination) == ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2)+2:
|
||||
destination = destination[1:-1]
|
||||
|
@ -269,8 +292,13 @@ class lxmf_connection:
|
|||
log("LXMF - Destination is invalid", LOG_ERROR)
|
||||
return
|
||||
|
||||
if destination_name == None:
|
||||
destination_name = self.destination_name
|
||||
if destination_type == None:
|
||||
destination_type = self.destination_type
|
||||
|
||||
destination_identity = RNS.Identity.recall(destination)
|
||||
destination = RNS.Destination(destination_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, self.destination_name, self.destination_type)
|
||||
destination = RNS.Destination(destination_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, destination_name, destination_type)
|
||||
self.send_message(destination, self.destination, content, title, fields, timestamp, app_data)
|
||||
|
||||
|
||||
|
@ -339,7 +367,7 @@ class lxmf_connection:
|
|||
message.desired_method_str = "propagated"
|
||||
|
||||
|
||||
def announce(self, initial=False):
|
||||
def announce(self, app_data=None, attached_interface=None, initial=False):
|
||||
announce_timer = None
|
||||
|
||||
if self.announce_periodic and self.announce_periodic_interval > 0:
|
||||
|
@ -356,26 +384,29 @@ class lxmf_connection:
|
|||
announce_timer.daemon = True
|
||||
announce_timer.start()
|
||||
else:
|
||||
self.announce_now()
|
||||
self.announce_now(app_data=app_data, attached_interface=attached_interface)
|
||||
return
|
||||
|
||||
self.announce_now()
|
||||
self.announce_now(app_data=app_data, attached_interface=attached_interface)
|
||||
|
||||
|
||||
def announce_now(self, app_data=None):
|
||||
if app_data:
|
||||
def announce_now(self, app_data=None, attached_interface=None):
|
||||
if self.announce_hidden:
|
||||
self.destination.announce("".encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +" (Hidden)", LOG_DEBUG)
|
||||
elif app_data != None:
|
||||
if isinstance(app_data, str):
|
||||
self.destination.announce(app_data.encode("utf-8"))
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + announce_data, LOG_DEBUG)
|
||||
self.destination.announce(app_data.encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + app_data, LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce(app_data)
|
||||
self.destination.announce(app_data, attached_interface=attached_interface)
|
||||
log("LMF - Announced: " + RNS.prettyhexrep(self.destination_hash()), LOG_DEBUG)
|
||||
elif self.announce_data:
|
||||
if isinstance(self.announce_data, str):
|
||||
self.destination.announce(self.announce_data.encode("utf-8"))
|
||||
self.destination.announce(self.announce_data.encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + self.announce_data, LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce(self.announce_data)
|
||||
self.destination.announce(self.announce_data, attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()), LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce()
|
||||
|
@ -417,24 +448,50 @@ class lxmf_connection:
|
|||
return False
|
||||
|
||||
|
||||
def autoselect_propagation_node(self):
|
||||
if self.propagation_node is not None:
|
||||
if len(self.propagation_node) != ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2):
|
||||
log("LXMF - Propagation node length is invalid", LOG_ERROR)
|
||||
else:
|
||||
try:
|
||||
propagation_hash = bytes.fromhex(self.propagation_node)
|
||||
except Exception as e:
|
||||
log("LXMF - Propagation node is invalid", LOG_ERROR)
|
||||
return
|
||||
def propagation_node_set(self, dest_str):
|
||||
if not dest_str:
|
||||
return False
|
||||
|
||||
node_identity = RNS.Identity.recall(propagation_hash)
|
||||
if node_identity != None:
|
||||
log("LXMF - Propagation node: " + RNS.prettyhexrep(propagation_hash), LOG_INFO)
|
||||
propagation_hash = RNS.Destination.hash_from_name_and_identity("lxmf.propagation", node_identity)
|
||||
self.message_router.set_outbound_propagation_node(propagation_hash)
|
||||
else:
|
||||
log("LXMF - Propagation node identity not known", LOG_ERROR)
|
||||
if len(dest_str) != ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2):
|
||||
log("LXMF - Propagation node length is invalid", LOG_ERROR)
|
||||
return False
|
||||
|
||||
try:
|
||||
dest_hash = bytes.fromhex(dest_str)
|
||||
except Exception as e:
|
||||
log("LXMF - Propagation node is invalid", LOG_ERROR)
|
||||
return False
|
||||
|
||||
node_identity = RNS.Identity.recall(dest_hash)
|
||||
if node_identity != None:
|
||||
log("LXMF - Propagation node: " + RNS.prettyhexrep(dest_hash), LOG_INFO)
|
||||
dest_hash = RNS.Destination.hash_from_name_and_identity("lxmf.propagation", node_identity)
|
||||
self.message_router.set_outbound_propagation_node(dest_hash)
|
||||
self.propagation_node_active = dest_str
|
||||
return True
|
||||
else:
|
||||
log("LXMF - Propagation node identity not known", LOG_ERROR)
|
||||
return False
|
||||
|
||||
|
||||
def propagation_node_update(self, dest_str):
|
||||
if self.propagation_node_hash_str() != dest_str:
|
||||
if self.propagation_node_set(dest_str) and self.config_set_callback is not None:
|
||||
self.config_set_callback("propagation_node_active", dest_str)
|
||||
|
||||
|
||||
def propagation_node_hash(self):
|
||||
try:
|
||||
return bytes.fromhex(self.propagation_node_active)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def propagation_node_hash_str(self):
|
||||
if self.propagation_node_active:
|
||||
return self.propagation_node_active
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def client_connected(self, link):
|
||||
|
@ -522,6 +579,43 @@ class lxmf_connection:
|
|||
log("- App Data: " + message.app_data, LOG_DEBUG)
|
||||
|
||||
|
||||
|
||||
|
||||
class lxmf_connection_propagation():
|
||||
def __init__(self, owner, aspect_filter=None):
|
||||
self.owner = owner
|
||||
self.aspect_filter = aspect_filter
|
||||
|
||||
EMITTED_DELTA_GRACE = 300
|
||||
EMITTED_DELTA_IGNORE = 10
|
||||
|
||||
def received_announce(self, destination_hash, announced_identity, app_data):
|
||||
if app_data == None:
|
||||
return
|
||||
|
||||
if len(app_data) == 0:
|
||||
return
|
||||
|
||||
try:
|
||||
unpacked = umsgpack.unpackb(app_data)
|
||||
node_active = unpacked[0]
|
||||
emitted = unpacked[1]
|
||||
hop_count = RNS.Transport.hops_to(destination_hash)
|
||||
age = time.time() - emitted
|
||||
if age < 0:
|
||||
if age < -1*PropDetector.EMITTED_DELTA_GRACE:
|
||||
return
|
||||
log("LXMF - Received an propagation node announce from "+RNS.prettyhexrep(destination_hash)+": "+str(age)+" seconds ago, "+str(hop_count)+" hops away", LOG_INFO)
|
||||
if self.owner.propagation_node_active == None:
|
||||
self.owner.propagation_node_update(RNS.hexrep(destination_hash, False))
|
||||
else:
|
||||
prev_hop_count = RNS.Transport.hops_to(self.owner.propagation_node_hash())
|
||||
if hop_count <= prev_hop_count:
|
||||
self.owner.propagation_node_update(RNS.hexrep(destination_hash, False))
|
||||
except:
|
||||
return
|
||||
|
||||
|
||||
##############################################################################################################
|
||||
# LXMF Functions
|
||||
|
||||
|
@ -575,15 +669,15 @@ def lxmf_failed(message):
|
|||
# Value convert
|
||||
|
||||
|
||||
def val_to_bool(val):
|
||||
def val_to_bool(val, fallback_true=True, fallback_false=False):
|
||||
if val == "on" or val == "On" or val == "true" or val == "True" or val == "yes" or val == "Yes" or val == "1" or val == "open" or val == "opened" or val == "up":
|
||||
return True
|
||||
elif val == "off" or val == "Off" or val == "false" or val == "False" or val == "no" or val == "No" or val == "0" or val == "close" or val == "closed" or val == "down":
|
||||
return False
|
||||
elif val != "":
|
||||
return True
|
||||
return fallback_true
|
||||
else:
|
||||
return False
|
||||
return fallback_false
|
||||
|
||||
|
||||
##############################################################################################################
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue