diff --git a/app/routes/api.py b/app/routes/api.py index 73a25e9..2743e4c 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -1680,6 +1680,21 @@ def get_dm_messages(): except Exception as e: logger.debug(f"Retry dedup failed (non-critical): {e}") + # Secondary dedup: collapse outgoing messages with same text+recipient + # within 120s window (catches retries whose ack wasn't tracked, e.g. timeouts) + deduped = [] + seen_outgoing = {} # (recipient, text) -> earliest timestamp + for msg in messages: + if msg.get('direction') == 'outgoing': + key = (msg.get('recipient', ''), msg.get('content', '')) + ts = msg.get('timestamp', 0) + prev_ts = seen_outgoing.get(key) + if prev_ts is not None and abs(ts - prev_ts) < 120: + continue # Skip duplicate within time window + seen_outgoing[key] = ts + deduped.append(msg) + messages = deduped + # Determine display name from conversation_id or messages display_name = 'Unknown' if conversation_id.startswith('pk_'): diff --git a/meshcore-bridge/bridge.py b/meshcore-bridge/bridge.py index 1de7ff3..b8e8e0f 100644 --- a/meshcore-bridge/bridge.py +++ b/meshcore-bridge/bridge.py @@ -921,11 +921,11 @@ class MeshCLISession: # Don't break - continue retrying (message was likely sent, # just couldn't parse ack due to timing) else: - logger.error(f"Retry: msg command failed: {result.get('stderr', '')}") - break + logger.warning(f"Retry: msg command failed: {result.get('stderr', '')}") + # Don't break - continue to next attempt (device may be temporarily busy) except Exception as e: - logger.error(f"Retry: send failed: {e}") - break + logger.warning(f"Retry: send exception: {e}") + # Don't break - continue to next attempt attempt += 1 if flood_mode: