Assume that a same-second same-message same-first-byte-key DM is more likely an echo than them sending the same message, and multi-retry for flood scope restoration

This commit is contained in:
Jack Kingsman
2026-04-10 15:50:45 -07:00
parent f6b0fd21fb
commit 59601bb98e
3 changed files with 178 additions and 28 deletions
+24 -3
View File
@@ -39,6 +39,7 @@ from app.repository import (
ChannelRepository,
ContactAdvertPathRepository,
ContactRepository,
MessageRepository,
RawPacketRepository,
)
from app.services.contact_reconciliation import (
@@ -645,10 +646,30 @@ async def _process_direct_message(
)
if result is not None:
# Successfully decrypted!
# In the ambiguous direction case (both first bytes match), we
# defaulted to incoming. Check if a matching outgoing message
# already exists — if so, this is actually our own outgoing echo
# and should be treated as such instead of creating a duplicate
# incoming row.
effective_outgoing = is_outgoing
if not is_outgoing and dest_hash == src_hash:
existing_outgoing = await MessageRepository.get_by_content(
msg_type="PRIV",
conversation_key=contact.public_key.lower(),
text=result.message,
sender_timestamp=result.timestamp,
outgoing=True,
)
if existing_outgoing is not None:
effective_outgoing = True
logger.debug(
"Ambiguous DM resolved as outgoing echo (matched existing sent msg %d)",
existing_outgoing.id,
)
logger.debug(
"Decrypted DM %s contact %s: %s",
"to" if is_outgoing else "from",
"to" if effective_outgoing else "from",
contact.name or contact.public_key[:12],
result.message[:50] if result.message else "",
)
@@ -664,7 +685,7 @@ async def _process_direct_message(
path_len=packet_info.path_length if packet_info else None,
rssi=rssi,
snr=snr,
outgoing=is_outgoing,
outgoing=effective_outgoing,
)
return {
+30 -25
View File
@@ -264,38 +264,43 @@ async def send_channel_message_with_effective_scope(
return send_result
finally:
if override_scope and override_scope != baseline_scope:
try:
restore_result = await mc.commands.set_flood_scope(
baseline_scope if baseline_scope else ""
)
if restore_result is not None and restore_result.type == EventType.ERROR:
logger.error(
"Failed to restore baseline flood_scope after sending to %s: %s",
restored = False
for attempt in range(3):
try:
restore_result = await mc.commands.set_flood_scope(
baseline_scope if baseline_scope else ""
)
if restore_result is not None and restore_result.type == EventType.ERROR:
logger.warning(
"Attempt %d/3: failed to restore flood_scope after sending to %s: %s",
attempt + 1,
channel.name,
restore_result.payload,
)
else:
logger.debug(
"Restored baseline flood_scope after channel send: %r",
baseline_scope or "(disabled)",
)
restored = True
break
except Exception:
logger.exception(
"Attempt %d/3: exception restoring flood_scope after sending to %s",
attempt + 1,
channel.name,
restore_result.payload,
)
error_broadcast_fn(
"Regional override restore failed",
(
f"Sent to {channel.name}, but restoring flood scope failed. "
"The radio may still be region-scoped. Consider rebooting the radio."
),
)
else:
logger.debug(
"Restored baseline flood_scope after channel send: %r",
baseline_scope or "(disabled)",
)
except Exception:
logger.exception(
"Failed to restore baseline flood_scope after sending to %s",
if not restored:
logger.error(
"All 3 attempts to restore flood_scope failed for %s",
channel.name,
)
error_broadcast_fn(
"Regional override restore failed",
(
f"Sent to {channel.name}, but restoring flood scope failed. "
"The radio may still be region-scoped. Consider rebooting the radio."
f"Sent to {channel.name}, but restoring flood scope failed "
f"after 3 attempts. The radio may still be region-scoped. "
f"Consider rebooting the radio."
),
)