140 lines
4.4 KiB
Python
140 lines
4.4 KiB
Python
import RNS
|
|
|
|
|
|
class RNPathHandler:
|
|
def __init__(self, reticulum_instance: RNS.Reticulum):
|
|
self.reticulum = reticulum_instance
|
|
|
|
def get_path_table(
|
|
self,
|
|
max_hops: int = None,
|
|
search: str = None,
|
|
interface: str = None,
|
|
hops: int = None,
|
|
page: int = 1,
|
|
limit: int = 0,
|
|
):
|
|
table = self.reticulum.get_path_table(max_hops=max_hops)
|
|
formatted_table = []
|
|
for entry in table:
|
|
# Get additional data directly from Transport.path_table if available
|
|
# to provide more stats as requested.
|
|
dst_hash = entry["hash"]
|
|
announce_hash = None
|
|
state = RNS.Transport.STATE_UNKNOWN
|
|
|
|
if dst_hash in RNS.Transport.path_table:
|
|
pt_entry = RNS.Transport.path_table[dst_hash]
|
|
if len(pt_entry) > 6:
|
|
announce_hash = pt_entry[6].hex() if pt_entry[6] else None
|
|
|
|
if dst_hash in RNS.Transport.path_states:
|
|
state = RNS.Transport.path_states[dst_hash]
|
|
|
|
# Filtering
|
|
if search:
|
|
search = search.lower()
|
|
hash_str = entry["hash"].hex().lower()
|
|
via_str = entry["via"].hex().lower()
|
|
if search not in hash_str and search not in via_str:
|
|
continue
|
|
|
|
if interface and entry["interface"] != interface:
|
|
continue
|
|
|
|
if hops is not None and entry["hops"] != hops:
|
|
continue
|
|
|
|
formatted_table.append(
|
|
{
|
|
"hash": entry["hash"].hex(),
|
|
"hops": entry["hops"],
|
|
"via": entry["via"].hex(),
|
|
"interface": entry["interface"],
|
|
"expires": entry["expires"],
|
|
"timestamp": entry.get("timestamp"),
|
|
"announce_hash": announce_hash,
|
|
"state": state,
|
|
},
|
|
)
|
|
|
|
# Sort: Responsive first, then by hops, then by interface
|
|
formatted_table.sort(
|
|
key=lambda e: (
|
|
0 if e["state"] == RNS.Transport.STATE_RESPONSIVE else 1,
|
|
e["hops"],
|
|
e["interface"],
|
|
),
|
|
)
|
|
|
|
total = len(formatted_table)
|
|
responsive_count = len(
|
|
[
|
|
e
|
|
for e in formatted_table
|
|
if e["state"] == RNS.Transport.STATE_RESPONSIVE
|
|
],
|
|
)
|
|
unresponsive_count = len(
|
|
[
|
|
e
|
|
for e in formatted_table
|
|
if e["state"] == RNS.Transport.STATE_UNRESPONSIVE
|
|
],
|
|
)
|
|
|
|
# Pagination
|
|
if limit > 0:
|
|
start = (page - 1) * limit
|
|
end = start + limit
|
|
formatted_table = formatted_table[start:end]
|
|
|
|
return {
|
|
"table": formatted_table,
|
|
"total": total,
|
|
"responsive": responsive_count,
|
|
"unresponsive": unresponsive_count,
|
|
"page": page,
|
|
"limit": limit,
|
|
}
|
|
|
|
def get_rate_table(self):
|
|
table = self.reticulum.get_rate_table()
|
|
formatted_table = [
|
|
{
|
|
"hash": entry["hash"].hex(),
|
|
"last": entry["last"],
|
|
"timestamps": entry["timestamps"],
|
|
"rate_violations": entry["rate_violations"],
|
|
"blocked_until": entry["blocked_until"],
|
|
}
|
|
for entry in table
|
|
]
|
|
return sorted(formatted_table, key=lambda e: e["last"])
|
|
|
|
def drop_path(self, destination_hash: str) -> bool:
|
|
try:
|
|
dest_bytes = bytes.fromhex(destination_hash)
|
|
return self.reticulum.drop_path(dest_bytes)
|
|
except Exception:
|
|
return False
|
|
|
|
def drop_all_via(self, transport_instance_hash: str) -> bool:
|
|
try:
|
|
ti_bytes = bytes.fromhex(transport_instance_hash)
|
|
return self.reticulum.drop_all_via(ti_bytes)
|
|
except Exception:
|
|
return False
|
|
|
|
def drop_announce_queues(self):
|
|
self.reticulum.drop_announce_queues()
|
|
return True
|
|
|
|
def request_path(self, destination_hash: str):
|
|
try:
|
|
dest_bytes = bytes.fromhex(destination_hash)
|
|
RNS.Transport.request_path(dest_bytes)
|
|
return True
|
|
except Exception:
|
|
return False
|