73 lines
3.6 KiB
Python
73 lines
3.6 KiB
Python
"""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
|