feat(dm): add delivery confirmation, retry, and receiver-side dedup

- Fix ACK handler bug: read 'code' field instead of 'expected_ack'
- Add DM retry (up to 3 attempts) with same timestamp for receiver dedup
- Add receiver-side dedup in _on_dm_received() (sender_timestamp or time-window)
- Add PATH_UPDATE as backup delivery signal for flood DMs
- Track pending acks with dm_id for proper ACK→DM linkage
- Return dm_id and expected_ack from POST /dm/messages API
- Add find_dm_duplicate() and get_dm_by_id() database helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-07 07:02:58 +01:00
parent c1b0085710
commit 5b757e9548
4 changed files with 258 additions and 40 deletions
+6 -6
View File
@@ -367,12 +367,12 @@ def floodadv() -> Tuple[bool, str]:
# Direct Messages
# =============================================================================
def send_dm(recipient: str, text: str) -> Tuple[bool, str]:
"""Send a direct message."""
def send_dm(recipient: str, text: str) -> Tuple[bool, Dict]:
"""Send a direct message. Returns (success, result_dict)."""
if not recipient or not recipient.strip():
return False, "Recipient is required"
return False, {'error': "Recipient is required"}
if not text or not text.strip():
return False, "Message text is required"
return False, {'error': "Message text is required"}
try:
dm = _get_dm()
@@ -387,9 +387,9 @@ def send_dm(recipient: str, text: str) -> Tuple[bool, str]:
pubkey = recipient.strip()
result = dm.send_dm(pubkey, text.strip())
return result['success'], result.get('message', result.get('error', ''))
return result['success'], result
except Exception as e:
return False, str(e)
return False, {'error': str(e)}
def check_dm_delivery(ack_codes: list) -> Tuple[bool, Dict, str]: