mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-11-04 03:52:34 +00:00
Seven places in message_handler and graph_trace_helper ran the same pair of queries, byte for byte: count the repeaters and room servers whose key starts with the prefix and were heard within the recency window, and if exactly one was, fetch its key (starred first, then most recent). contacts_repo.unique_recent_repeater_key now runs it and returns (count, key); each caller keeps its own logging, including the two that report the match count. Same queries, same order, same handling of execute_query's empty result on a database error.
252 lines
10 KiB
Python
252 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Graph update helper for trace data.
|
|
Shared by message_handler (on RX) and trace command (when TRACE_DATA is received).
|
|
"""
|
|
|
|
import time
|
|
from typing import Any, Optional
|
|
|
|
from .contacts_repo import unique_recent_repeater_key
|
|
|
|
|
|
def update_mesh_graph_from_trace_data(
|
|
bot: Any,
|
|
path_hashes: list[str],
|
|
packet_info: dict[str, Any],
|
|
*,
|
|
is_our_trace: Optional[bool] = None,
|
|
) -> None:
|
|
"""Update mesh graph with edges from a trace packet's pathHashes.
|
|
|
|
When the bot receives a trace packet, it's the destination, so we can confirm
|
|
the edges in the path. The pathHashes represent the routing path the packet took.
|
|
|
|
Special case: If this is a trace we sent that came back through an immediate neighbor,
|
|
we can trust both directions (Bot -> Neighbor and Neighbor -> Bot).
|
|
|
|
Args:
|
|
bot: MeshCoreBot instance (mesh_graph, transmission_tracker, config, db_manager, meshcore).
|
|
path_hashes: Per-hop hash strings from the trace payload (uppercase hex). Length in nibbles is
|
|
``2 * (1 << (flags & 3))`` per hop (1, 2, 4, or 8 bytes), not always 2 hex chars.
|
|
packet_info: Packet information dictionary (packet_hash optional; used when is_our_trace is None).
|
|
is_our_trace: If None, derived from packet_info['packet_hash'] and transmission_tracker.
|
|
If True/False, use that value (e.g. trace command sets True when TRACE_DATA matches our tag).
|
|
"""
|
|
if not path_hashes or len(path_hashes) == 0:
|
|
bot.logger.debug("Mesh graph: Trace packet has no pathHashes, skipping graph update")
|
|
return
|
|
|
|
if not hasattr(bot, "mesh_graph") or not bot.mesh_graph:
|
|
bot.logger.debug("Mesh graph: Graph not initialized, skipping trace update")
|
|
return
|
|
|
|
if not hasattr(bot, "transmission_tracker") or not bot.transmission_tracker:
|
|
bot.logger.debug("Mesh graph: Cannot get bot prefix, skipping trace update")
|
|
return
|
|
|
|
mesh_graph = bot.mesh_graph
|
|
bot_prefix = bot.transmission_tracker.bot_prefix
|
|
|
|
if not bot_prefix:
|
|
bot.logger.debug("Mesh graph: Bot prefix not available, skipping trace update")
|
|
return
|
|
|
|
bot_prefix = bot_prefix.lower()
|
|
|
|
# Resolve is_our_trace
|
|
if is_our_trace is None:
|
|
is_our_trace = False
|
|
packet_hash = packet_info.get("packet_hash")
|
|
if packet_hash and hasattr(bot, "transmission_tracker"):
|
|
record = bot.transmission_tracker.match_packet_hash(packet_hash, time.time())
|
|
if record:
|
|
is_our_trace = True
|
|
bot.logger.debug("Mesh graph: Trace packet is one we sent (matched transmission record)")
|
|
|
|
# Check if this came back through an immediate neighbor
|
|
is_immediate_neighbor = False
|
|
if is_our_trace and len(path_hashes) == 1:
|
|
is_immediate_neighbor = True
|
|
bot.logger.info(
|
|
f"Mesh graph: Trace came back through immediate neighbor {path_hashes[0]} - trusting both directions"
|
|
)
|
|
|
|
bot.logger.debug(
|
|
f"Mesh graph: Updating graph from trace pathHashes: {path_hashes} "
|
|
f"(bot is destination: {bot_prefix}, is_our_trace: {is_our_trace}, immediate_neighbor: {is_immediate_neighbor})"
|
|
)
|
|
|
|
recency_days = bot.config.getint("Path_Command", "graph_edge_expiration_days", fallback=7)
|
|
|
|
from .utils import _get_node_location_from_db, calculate_distance
|
|
|
|
bot_location = None
|
|
try:
|
|
bot_location_result = _get_node_location_from_db(bot, bot_prefix, None, recency_days)
|
|
if bot_location_result:
|
|
bot_location, _ = bot_location_result
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not get bot location: {e}")
|
|
|
|
if len(path_hashes) == 0:
|
|
return
|
|
|
|
last_node = path_hashes[-1].lower()
|
|
|
|
if is_immediate_neighbor:
|
|
neighbor_prefix = path_hashes[0].lower()
|
|
neighbor_key = None
|
|
try:
|
|
_, unique_key = unique_recent_repeater_key(bot.db_manager, neighbor_prefix, recency_days)
|
|
if unique_key:
|
|
neighbor_key = unique_key
|
|
except Exception as e:
|
|
bot.logger.debug(f"Error checking uniqueness for immediate neighbor {neighbor_prefix}: {e}")
|
|
|
|
bot_key = None
|
|
if hasattr(bot, "meshcore") and bot.meshcore and getattr(bot.meshcore, "device", None):
|
|
try:
|
|
device_info = bot.meshcore.device
|
|
if hasattr(device_info, "public_key"):
|
|
pubkey = device_info.public_key
|
|
if isinstance(pubkey, str):
|
|
bot_key = pubkey
|
|
elif isinstance(pubkey, bytes):
|
|
bot_key = pubkey.hex()
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not get bot public key: {e}")
|
|
|
|
geographic_distance = None
|
|
try:
|
|
if bot_location:
|
|
neighbor_result = _get_node_location_from_db(bot, neighbor_prefix, bot_location, recency_days)
|
|
if neighbor_result:
|
|
neighbor_location, selected_neighbor_key = neighbor_result
|
|
if not neighbor_key and selected_neighbor_key:
|
|
neighbor_key = selected_neighbor_key
|
|
if neighbor_location and bot_location:
|
|
geographic_distance = calculate_distance(
|
|
neighbor_location[0], neighbor_location[1],
|
|
bot_location[0], bot_location[1],
|
|
)
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not calculate distance for immediate neighbor edge: {e}")
|
|
|
|
mesh_graph.add_edge(
|
|
from_prefix=bot_prefix,
|
|
to_prefix=neighbor_prefix,
|
|
from_public_key=bot_key,
|
|
to_public_key=neighbor_key,
|
|
hop_position=1,
|
|
geographic_distance=geographic_distance,
|
|
)
|
|
mesh_graph.add_edge(
|
|
from_prefix=neighbor_prefix,
|
|
to_prefix=bot_prefix,
|
|
from_public_key=neighbor_key,
|
|
to_public_key=bot_key,
|
|
hop_position=1,
|
|
geographic_distance=geographic_distance,
|
|
)
|
|
bot.logger.info(f"Mesh graph: Created trusted bidirectional edge with immediate neighbor {neighbor_prefix}")
|
|
return
|
|
|
|
# Regular case: trace from elsewhere, bot is destination
|
|
geographic_distance = None
|
|
last_node_key = None
|
|
try:
|
|
_, unique_key = unique_recent_repeater_key(bot.db_manager, last_node, recency_days)
|
|
if unique_key:
|
|
last_node_key = unique_key
|
|
except Exception as e:
|
|
bot.logger.debug(f"Error checking uniqueness for trace last_node {last_node}: {e}")
|
|
|
|
bot_key = None
|
|
if hasattr(bot, "meshcore") and bot.meshcore and getattr(bot.meshcore, "device", None):
|
|
try:
|
|
device_info = bot.meshcore.device
|
|
if hasattr(device_info, "public_key"):
|
|
pubkey = device_info.public_key
|
|
if isinstance(pubkey, str):
|
|
bot_key = pubkey
|
|
elif isinstance(pubkey, bytes):
|
|
bot_key = pubkey.hex()
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not get bot public key: {e}")
|
|
|
|
try:
|
|
if bot_location:
|
|
last_node_result = _get_node_location_from_db(bot, last_node, bot_location, recency_days)
|
|
if last_node_result:
|
|
last_node_location, selected_key = last_node_result
|
|
if not last_node_key and selected_key:
|
|
last_node_key = selected_key
|
|
if last_node_location and bot_location:
|
|
geographic_distance = calculate_distance(
|
|
last_node_location[0], last_node_location[1],
|
|
bot_location[0], bot_location[1],
|
|
)
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not calculate distance for trace edge {last_node}->{bot_prefix}: {e}")
|
|
|
|
mesh_graph.add_edge(
|
|
from_prefix=last_node,
|
|
to_prefix=bot_prefix,
|
|
from_public_key=last_node_key,
|
|
to_public_key=bot_key,
|
|
hop_position=len(path_hashes),
|
|
geographic_distance=geographic_distance,
|
|
)
|
|
|
|
# Create edges between nodes in the pathHashes (if more than one)
|
|
previous_location = bot_location
|
|
for i in range(len(path_hashes) - 1, 0, -1):
|
|
from_node = path_hashes[i - 1].lower()
|
|
to_node = path_hashes[i].lower()
|
|
hop_position = len(path_hashes) - i
|
|
|
|
from_node_key = None
|
|
to_node_key = None
|
|
for node, key_var in [(from_node, "from_node_key"), (to_node, "to_node_key")]:
|
|
try:
|
|
_, unique_key = unique_recent_repeater_key(bot.db_manager, node, recency_days)
|
|
if unique_key:
|
|
if key_var == "from_node_key":
|
|
from_node_key = unique_key
|
|
else:
|
|
to_node_key = unique_key
|
|
except Exception as e:
|
|
bot.logger.debug(f"Error checking uniqueness for trace node {node}: {e}")
|
|
|
|
geographic_distance = None
|
|
try:
|
|
if previous_location:
|
|
from_result = _get_node_location_from_db(bot, from_node, previous_location, recency_days)
|
|
if from_result:
|
|
from_location, selected_from_key = from_result
|
|
if not from_node_key and selected_from_key:
|
|
from_node_key = selected_from_key
|
|
to_result = _get_node_location_from_db(bot, to_node, from_location, recency_days)
|
|
if to_result:
|
|
to_location, selected_to_key = to_result
|
|
if not to_node_key and selected_to_key:
|
|
to_node_key = selected_to_key
|
|
if from_location and to_location:
|
|
geographic_distance = calculate_distance(
|
|
from_location[0], from_location[1],
|
|
to_location[0], to_location[1],
|
|
)
|
|
previous_location = from_location
|
|
except Exception as e:
|
|
bot.logger.debug(f"Could not calculate distance for trace edge {from_node}->{to_node}: {e}")
|
|
|
|
mesh_graph.add_edge(
|
|
from_prefix=from_node,
|
|
to_prefix=to_node,
|
|
from_public_key=from_node_key,
|
|
to_public_key=to_node_key,
|
|
hop_position=hop_position,
|
|
geographic_distance=geographic_distance,
|
|
)
|