fix(dm): refresh mc.contacts on approve, DB name fallback, relink orphans

Three fixes for DM sending after contact delete/re-add:

1. approve_contact() now calls ensure_contacts() to refresh mc.contacts
   so send_dm can find newly added contacts immediately

2. cli.send_dm() falls back to DB name lookup when mc.contacts misses,
   preventing the contact name from being passed as a pubkey string

3. approve_contact() re-links orphaned DMs (NULL contact_pubkey from
   ON DELETE SET NULL) back to the re-added contact

New DB methods: get_contact_by_name(), relink_orphaned_dms()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-07 07:41:15 +01:00
parent 1a3a1e937c
commit d1ce3ceb92
3 changed files with 49 additions and 3 deletions
+29
View File
@@ -137,6 +137,15 @@ class Database:
).fetchone()
return dict(row) if row else None
def get_contact_by_name(self, name: str) -> Optional[Dict]:
"""Find a contact by exact name match."""
with self._connect() as conn:
row = conn.execute(
"SELECT * FROM contacts WHERE name = ? AND length(public_key) = 64 LIMIT 1",
(name,)
).fetchone()
return dict(row) if row else None
def delete_contact(self, public_key: str) -> bool:
with self._connect() as conn:
cursor = conn.execute(
@@ -373,6 +382,26 @@ class Database:
).fetchone()
return dict(row) if row else None
def relink_orphaned_dms(self, public_key: str) -> int:
"""Re-link DMs with NULL contact_pubkey back to this contact.
When a contact is deleted, ON DELETE SET NULL nullifies contact_pubkey.
When the contact is re-added, re-link those orphaned DMs.
Uses raw_json to match by pubkey_prefix.
"""
public_key = public_key.lower()
prefix = public_key[:12] # Short prefix used in pubkey_prefix field
with self._connect() as conn:
cursor = conn.execute(
"""UPDATE direct_messages SET contact_pubkey = ?
WHERE contact_pubkey IS NULL
AND (raw_json LIKE ? OR raw_json IS NULL)""",
(public_key, f'%{prefix}%')
)
if cursor.rowcount > 0:
logger.info(f"Re-linked {cursor.rowcount} orphaned DMs to {public_key[:12]}...")
return cursor.rowcount
def find_dm_duplicate(self, contact_pubkey: str, content: str,
sender_timestamp: int = None,
window_seconds: int = 300) -> Optional[Dict]: