"""Current account's customer relation snapshot; never infers by name or chat text.""" from pathlib import Path import math import sqlite3 from review_assistant_contacts import recipient_peer def unknown_relation(account, conv_id, reason="unavailable"): return {"account": str(account), "conv_id": str(conv_id), "peer_id": recipient_peer(account, conv_id), "state": "unknown", "added_at": 0.0, "reason": reason} def read_contact_relation(path, account, conv_id): """Read only a supplied, account-bound current snapshot, never search caches. An active local customer entry is not proof of successful remote delivery. The protocol engine combines it with a genuinely new customer event before reconsidering a past rejection, and still requires the native send receipt. """ result = unknown_relation(account, conv_id) peer = result["peer_id"] if not peer or not path: return result path = Path(path) try: if path.name != "user.db" or path.resolve().parent.name != str(account): return {**result, "reason": "account_mismatch"} connection = sqlite3.connect(path.resolve().as_uri() + "?mode=ro", uri=True, timeout=.2) try: connection.execute("PRAGMA query_only=ON") connection.execute("BEGIN") required = { "external_user_relation_v3": {"user_id", "status", "stranger_type", "add_customer_time"}, "delete_external_userV1": {"user_id"}, "blacklist_external_userids": {"value"}, } for table, fields in required.items(): columns = {row[1] for row in connection.execute(f'PRAGMA table_info("{table}")')} if not fields.issubset(columns): return {**result, "reason": "schema_unverified"} deleted = connection.execute("SELECT 1 FROM delete_external_userV1 WHERE user_id=? LIMIT 1", (peer,)).fetchone() denied = connection.execute("SELECT 1 FROM blacklist_external_userids WHERE value=? LIMIT 1", (peer,)).fetchone() if deleted or denied: return {**result, "state": "blocked", "reason": "deleted_or_blocklisted"} rows = connection.execute( "SELECT status,stranger_type,add_customer_time FROM external_user_relation_v3 WHERE user_id=? LIMIT 2", (peer,)).fetchall() if len(rows) != 1: return {**result, "reason": "relation_missing_or_ambiguous"} status, stranger, added = rows[0] # The allowlist matches the supported WeCom build. Unknown flags # never grant recovery, and a prior add time is not a re-add time. if type(status) is not int or type(stranger) is not int: return {**result, "reason": "status_unverified"} if stranger != 0: return {**result, "state": "blocked", "reason": "stranger"} if status not in {2049, 2057}: return {**result, "reason": "status_unverified"} if not isinstance(added, (int, float)) or isinstance(added, bool): return {**result, "reason": "add_time_unverified"} added = float(added) if added > 10**12: added /= 1000 if not math.isfinite(added) or added <= 0: return {**result, "reason": "add_time_unverified"} return {**result, "state": "active", "added_at": added, "reason": "current_customer"} finally: connection.close() except (OSError, ValueError, sqlite3.Error): return result