mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-07-05 09:21:13 +02:00
fix(channels): skip self-echoes of raw resends in _on_channel_message
CMD_SEND_RAW_PACKET bypasses sendFlood()'s _tables->hasSeen() self-mark. The firmware seen-table is 160 entries in RAM and rolls over in minutes on a busy mesh, so a resend issued after the original hash got evicted comes back via repeater echo, fails hasSeen (entry gone), and is delivered as RESP_CODE_CHANNEL_MSG_RECV. Result: the user's own resend appears as an "incoming" message from themselves a few minutes later. Detection: in _on_channel_message compute the expected pkt_payload from the event's (channel_secret, sender_timestamp, txt_type, raw_text) and ask the DB if any own row already has that exact hash. If yes, treat as self-echo and return — no DB insert, no SocketIO emit. Index idx_cm_pkt keeps the lookup cheap. Guarded with try/except so any detection failure falls through to normal handling — we never want a check bug to drop real inbound messages. Documented as a known limitation in reference_meshcore_raw_packet.md; this lifts that caveat. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1001,6 +1001,24 @@ class Database:
|
||||
(raw_packet, msg_id)
|
||||
)
|
||||
|
||||
def has_own_channel_message_with_pkt_payload(self, pkt_payload: str) -> bool:
|
||||
"""Check if we sent a channel message with the given pkt_payload.
|
||||
|
||||
Used by the CHANNEL_MSG_RECV handler to detect self-echoes of raw
|
||||
resends — when CMD_SEND_RAW_PACKET bypasses firmware's hasSeen mark
|
||||
and an echo from a repeater is processed as a "new" incoming
|
||||
message. Index `idx_cm_pkt` makes the lookup O(log n).
|
||||
"""
|
||||
if not pkt_payload:
|
||||
return False
|
||||
with self._connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT 1 FROM channel_messages "
|
||||
"WHERE pkt_payload = ? AND is_own = 1 LIMIT 1",
|
||||
(pkt_payload,)
|
||||
).fetchone()
|
||||
return row is not None
|
||||
|
||||
# ================================================================
|
||||
# Paths
|
||||
# ================================================================
|
||||
|
||||
@@ -720,6 +720,42 @@ class DeviceManager:
|
||||
sender = 'Unknown'
|
||||
content = raw_text
|
||||
|
||||
# Self-echo guard for raw resends.
|
||||
#
|
||||
# CMD_SEND_RAW_PACKET (used by /api/messages/<id>/resend) bypasses
|
||||
# firmware's sendFlood() and therefore the _tables->hasSeen()
|
||||
# self-mark. The 160-entry seen-table is RAM-only and rolls over
|
||||
# in minutes on a busy mesh, so a resend issued after the original's
|
||||
# hash got evicted comes back via repeater echo, passes hasSeen
|
||||
# ("not seen"), and is delivered to companion as RESP_CODE_CHANNEL_MSG_RECV.
|
||||
# Without this guard the user sees their own resend pop up as an
|
||||
# incoming message from themselves.
|
||||
#
|
||||
# Compute the same pkt_payload we would have stored for the original
|
||||
# send and ask the DB if we already have an own row with that hash.
|
||||
sender_ts = data.get('sender_timestamp')
|
||||
txt_type = data.get('txt_type', 0)
|
||||
if sender_ts and sender == self.device_name:
|
||||
try:
|
||||
secret = self._channel_secrets.get(channel_idx)
|
||||
if secret:
|
||||
expected_pkt_payload = _compute_pkt_payload(
|
||||
secret, sender_ts, txt_type, raw_text
|
||||
)
|
||||
if self.db.has_own_channel_message_with_pkt_payload(
|
||||
expected_pkt_payload
|
||||
):
|
||||
logger.info(
|
||||
f"Self-echo of own ch{channel_idx} msg detected "
|
||||
f"(sender_ts={sender_ts}) — stale resend "
|
||||
f"firmware-loopback. Skipping insertion."
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
# Don't let detection failures drop legitimate inbound
|
||||
# messages — fall through to normal handling.
|
||||
logger.debug(f"Self-echo check inconclusive: {e}")
|
||||
|
||||
# Check if sender is blocked (store but don't emit)
|
||||
blocked_names = self.db.get_blocked_contact_names()
|
||||
is_blocked = sender in blocked_names
|
||||
|
||||
Reference in New Issue
Block a user