mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
fix(dm): Fix unread markers by deduping retries in all endpoints
Extracted dedup_retry_messages() helper (300s window, was 120s which was too tight) and applied it in three places: - GET /api/dm/messages - already had inline dedup, now uses helper - get_dm_conversations() - fixes last_message_timestamp inflation - GET /api/dm/updates - was missing dedup entirely, counted retries as unread messages (root cause of persistent unread markers) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -559,6 +559,26 @@ def read_dm_messages(
|
||||
return messages, pubkey_to_name
|
||||
|
||||
|
||||
def dedup_retry_messages(messages: List[Dict], window_seconds: int = 300) -> List[Dict]:
|
||||
"""Collapse outgoing messages with same text+recipient within a time window.
|
||||
|
||||
Auto-retry sends multiple SENT_MSG entries for the same message.
|
||||
This keeps only the first occurrence and drops duplicates within the window.
|
||||
"""
|
||||
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) <= window_seconds:
|
||||
continue
|
||||
seen_outgoing[key] = ts
|
||||
deduped.append(msg)
|
||||
return deduped
|
||||
|
||||
|
||||
def get_dm_conversations(days: Optional[int] = 7) -> List[Dict]:
|
||||
"""
|
||||
Get list of DM conversations with metadata.
|
||||
@@ -582,19 +602,7 @@ def get_dm_conversations(days: Optional[int] = 7) -> List[Dict]:
|
||||
"""
|
||||
messages, pubkey_to_name = read_dm_messages(days=days)
|
||||
|
||||
# Deduplicate outgoing retry messages: collapse same text+recipient within 120s
|
||||
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
|
||||
seen_outgoing[key] = ts
|
||||
deduped.append(msg)
|
||||
messages = deduped
|
||||
messages = dedup_retry_messages(messages)
|
||||
|
||||
# Build reverse mapping: name -> pubkey_prefix
|
||||
name_to_pubkey = {name: pk for pk, name in pubkey_to_name.items()}
|
||||
|
||||
@@ -1680,20 +1680,8 @@ 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
|
||||
# Secondary dedup: collapse retries with same text+recipient within 5min window
|
||||
messages = parser.dedup_retry_messages(messages)
|
||||
|
||||
# Determine display name from conversation_id or messages
|
||||
display_name = 'Unknown'
|
||||
@@ -1895,11 +1883,12 @@ def get_dm_updates():
|
||||
|
||||
# Count unread
|
||||
if conv['last_message_timestamp'] > last_seen_ts:
|
||||
# Need to count actual unread messages
|
||||
# Need to count actual unread messages (dedup retries)
|
||||
messages, _ = parser.read_dm_messages(
|
||||
conversation_id=conv_id,
|
||||
days=7
|
||||
)
|
||||
messages = parser.dedup_retry_messages(messages)
|
||||
unread_count = sum(1 for m in messages if m['timestamp'] > last_seen_ts)
|
||||
else:
|
||||
unread_count = 0
|
||||
|
||||
Reference in New Issue
Block a user