fix(dm): Wait for msg JSON response and fix unread markers

Monitor: For .msg commands, keep waiting for expected_ack in response
instead of completing on silence timeout. Waits up to half of cmd_timeout
(5s) for the JSON to arrive before giving up.

Unread: Apply 120s time-window dedup to outgoing messages in conversation
listing, so retry messages don't inflate last_message_timestamp and cause
permanent unread markers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-02-25 10:19:28 +01:00
parent 49159a888c
commit c2acbb4ba1
2 changed files with 26 additions and 4 deletions

View File

@@ -582,6 +582,20 @@ 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
# Build reverse mapping: name -> pubkey_prefix
name_to_pubkey = {name: pk for pk, name in pubkey_to_name.items()}

View File

@@ -457,17 +457,16 @@ class MeshCLISession:
# because meshcli may echo prompt immediately but real results come later
is_slow_command = cmd_timeout >= 15
# For .msg/.m commands, JSON response (with expected_ack) may arrive after prompt echo
# with a gap > 300ms, so enforce a minimum wait to capture it
# with a gap > 300ms, so keep waiting until JSON arrives or timeout
cmd_str = response_dict.get("command", "")
is_msg_command = cmd_str.lstrip('.').split()[0] in ('msg', 'm') if cmd_str.strip() else False
if is_slow_command:
min_elapsed = cmd_timeout * 0.7
elif is_msg_command:
min_elapsed = 1.5 # Wait at least 1.5s for JSON response
else:
min_elapsed = 0
logger.info(f"Monitor [{cmd_id}] started, cmd_timeout={cmd_timeout}s, min_elapsed={min_elapsed:.1f}s")
logger.info(f"Monitor [{cmd_id}] started, cmd_timeout={cmd_timeout}s, "
f"min_elapsed={min_elapsed:.1f}s, is_msg={is_msg_command}")
while not self.shutdown_flag.is_set():
time.sleep(timeout_ms / 1000.0)
@@ -481,6 +480,15 @@ class MeshCLISession:
time_since_last_line = time.time() - response_dict.get("last_line_time", 0)
total_elapsed = time.time() - start_time
has_output = len(response_dict.get("response", [])) > 0
response_lines = response_dict.get("response", [])
# For .msg commands: don't complete until we see expected_ack in response
# or we've waited long enough (half of cmd_timeout)
if is_msg_command and has_output:
response_text = '\n'.join(response_lines)
has_json = 'expected_ack' in response_text
if not has_json and total_elapsed < cmd_timeout * 0.5:
continue # Keep waiting for JSON response
# Can only complete if:
# 1. Minimum elapsed time has passed (for slow commands), AND