diff --git a/host-bridge/bridge_core.py b/host-bridge/bridge_core.py index 90e21f0..9656d72 100644 --- a/host-bridge/bridge_core.py +++ b/host-bridge/bridge_core.py @@ -56,5 +56,21 @@ class MessageBus: self._seen[h] = now + self._dedup_ttl return False + def mark_seen(self, text: str) -> None: + """Record text as already-seen without reporting duplicate status. + + The dispatcher relays a *prefixed* copy of each message (e.g. + "[meshtastic/x] hello"), not the original text. Dedup is content-hash + based, so unless the exact on-air relayed string is recorded here, the + bridge won't recognise its own relayed copy when it echoes back from + another network — it would re-relay it, stacking another prefix each + hop (an unbounded loop). Marking every outbound string seen closes + that loop. + """ + h = self._content_hash(text) + now = time.monotonic() + with self._lock: + self._seen[h] = now + self._dedup_ttl + def get(self, timeout: float | None = None) -> BridgeMessage: return self._queue.get(timeout=timeout) diff --git a/host-bridge/main.py b/host-bridge/main.py index 99f51a1..abddd17 100644 --- a/host-bridge/main.py +++ b/host-bridge/main.py @@ -82,11 +82,16 @@ def main() -> int: if bus.is_duplicate(message.text): continue print(f"[{message.network}] {message.sender}: {message.text}") + relayed = f"[{message.network}/{message.sender}] {message.text}" + # Record the exact on-air string so that if this relayed copy is + # re-heard from another network it's recognised as a duplicate and + # not re-relayed (which would stack another prefix each hop). + bus.mark_seen(relayed) for network, adapter in adapters.items(): if network == message.network: continue # don't echo back onto the network it came from try: - adapter.send(f"[{message.network}/{message.sender}] {message.text}") + adapter.send(relayed) except Exception as exc: # noqa: BLE001 print(f" ! failed to relay onto {network}: {exc}", file=sys.stderr) except KeyboardInterrupt: