fix(backend): profile history gets a row only when a field really changed, partial snapshots keep the known phone and username
This commit is contained in:
1 parent
aa41bbbc74
commit
0c9c848fb1
2 files changed
+53
-22
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from pyrogram import Client, raw
|
from pyrogram import Client, raw
|
||||||
from pyrogram.types import Chat, User
|
from pyrogram.types import Chat, User
|
||||||
@@ -12,6 +12,11 @@ class ProfileFields:
|
|||||||
phone: str | None = None
|
phone: str | None = None
|
||||||
photo_unique_id: str | None = None
|
photo_unique_id: str | None = None
|
||||||
is_deleted_account: bool = False
|
is_deleted_account: bool = False
|
||||||
|
unknown: frozenset[str] = field(default=frozenset(), compare=False)
|
||||||
|
|
||||||
|
|
||||||
|
MIN_UNKNOWN = frozenset({"username", "phone"})
|
||||||
|
CHAT_UNKNOWN = frozenset({"phone", "is_deleted_account"})
|
||||||
|
|
||||||
|
|
||||||
def active_username(usernames: list[raw.types.Username]) -> str | None:
|
def active_username(usernames: list[raw.types.Username]) -> str | None:
|
||||||
@@ -35,6 +40,7 @@ async def snapshot_from_user(
|
|||||||
phone=user.phone_number if user else None,
|
phone=user.phone_number if user else None,
|
||||||
photo_unique_id=photo_unique_id,
|
photo_unique_id=photo_unique_id,
|
||||||
is_deleted_account=bool(getattr(raw_user, "deleted", False)),
|
is_deleted_account=bool(getattr(raw_user, "deleted", False)),
|
||||||
|
unknown=MIN_UNKNOWN if getattr(raw_user, "min", False) else frozenset(),
|
||||||
)
|
)
|
||||||
return fields, photo_file_id, photo_unique_id
|
return fields, photo_file_id, photo_unique_id
|
||||||
|
|
||||||
@@ -52,6 +58,7 @@ def snapshot_from_high_level(
|
|||||||
phone=user.phone_number,
|
phone=user.phone_number,
|
||||||
photo_unique_id=photo_unique_id,
|
photo_unique_id=photo_unique_id,
|
||||||
is_deleted_account=bool(user.is_deleted),
|
is_deleted_account=bool(user.is_deleted),
|
||||||
|
unknown=MIN_UNKNOWN if user.is_min else frozenset(),
|
||||||
)
|
)
|
||||||
return fields, photo_file_id, photo_unique_id
|
return fields, photo_file_id, photo_unique_id
|
||||||
|
|
||||||
@@ -64,8 +71,7 @@ def snapshot_from_chat(chat: Chat) -> tuple[ProfileFields, str | None, str | Non
|
|||||||
first_name=chat.first_name,
|
first_name=chat.first_name,
|
||||||
last_name=chat.last_name,
|
last_name=chat.last_name,
|
||||||
username=chat.username,
|
username=chat.username,
|
||||||
phone=None,
|
|
||||||
photo_unique_id=photo_unique_id,
|
photo_unique_id=photo_unique_id,
|
||||||
is_deleted_account=False,
|
unknown=CHAT_UNKNOWN,
|
||||||
)
|
)
|
||||||
return fields, photo_file_id, photo_unique_id
|
return fields, photo_file_id, photo_unique_id
|
||||||
@@ -9,26 +9,46 @@ WHERE account_id = $1 AND peer_id = $2
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_UPSERT_PEER = """
|
_UPSERT_PEER = """
|
||||||
INSERT INTO peers
|
INSERT INTO peers AS p
|
||||||
(account_id, peer_id, first_name, last_name, username, phone,
|
(account_id, peer_id, first_name, last_name, username, phone,
|
||||||
photo_unique_id, is_deleted_account, raw, updated_at)
|
photo_unique_id, is_deleted_account, raw, updated_at)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, now())
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, now())
|
||||||
ON CONFLICT (account_id, peer_id) DO UPDATE SET
|
ON CONFLICT (account_id, peer_id) DO UPDATE SET
|
||||||
first_name = EXCLUDED.first_name,
|
first_name = EXCLUDED.first_name,
|
||||||
last_name = EXCLUDED.last_name,
|
last_name = EXCLUDED.last_name,
|
||||||
username = EXCLUDED.username,
|
username = CASE WHEN 'username' = ANY($10::text[])
|
||||||
phone = EXCLUDED.phone,
|
THEN COALESCE(EXCLUDED.username, p.username) ELSE EXCLUDED.username END,
|
||||||
|
phone = CASE WHEN 'phone' = ANY($10::text[])
|
||||||
|
THEN COALESCE(EXCLUDED.phone, p.phone) ELSE EXCLUDED.phone END,
|
||||||
photo_unique_id = EXCLUDED.photo_unique_id,
|
photo_unique_id = EXCLUDED.photo_unique_id,
|
||||||
is_deleted_account = EXCLUDED.is_deleted_account,
|
is_deleted_account = CASE WHEN 'is_deleted_account' = ANY($10::text[])
|
||||||
|
THEN p.is_deleted_account ELSE EXCLUDED.is_deleted_account END,
|
||||||
raw = EXCLUDED.raw,
|
raw = EXCLUDED.raw,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_INSERT_HISTORY = """
|
_INSERT_CHANGE = """
|
||||||
INSERT INTO peer_history
|
INSERT INTO peer_history
|
||||||
(account_id, peer_id, observed_at, first_name, last_name, username, phone,
|
(account_id, peer_id, observed_at, first_name, last_name, username, phone,
|
||||||
photo_unique_id, is_deleted_account, raw)
|
photo_unique_id, is_deleted_account, raw)
|
||||||
VALUES ($1, $2, now(), $3, $4, $5, $6, $7, $8, $9::jsonb)
|
SELECT account_id, peer_id, clock_timestamp(), first_name, last_name, username,
|
||||||
|
phone, photo_unique_id, is_deleted_account, raw
|
||||||
|
FROM peers p
|
||||||
|
WHERE account_id = $1 AND peer_id = $2
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM (
|
||||||
|
SELECT first_name, last_name, username, phone, photo_unique_id,
|
||||||
|
is_deleted_account
|
||||||
|
FROM peer_history
|
||||||
|
WHERE account_id = $1 AND peer_id = $2
|
||||||
|
ORDER BY observed_at DESC LIMIT 1
|
||||||
|
) latest
|
||||||
|
WHERE (latest.first_name, latest.last_name, latest.username, latest.phone,
|
||||||
|
latest.photo_unique_id, latest.is_deleted_account)
|
||||||
|
IS NOT DISTINCT FROM
|
||||||
|
(p.first_name, p.last_name, p.username, p.phone,
|
||||||
|
p.photo_unique_id, p.is_deleted_account)
|
||||||
|
)
|
||||||
ON CONFLICT DO NOTHING
|
ON CONFLICT DO NOTHING
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -57,17 +77,22 @@ async def write_profile( # noqa: PLR0913
|
|||||||
raw: str,
|
raw: str,
|
||||||
*,
|
*,
|
||||||
history: bool,
|
history: bool,
|
||||||
) -> None:
|
) -> bool:
|
||||||
args = (
|
|
||||||
fields.first_name,
|
|
||||||
fields.last_name,
|
|
||||||
fields.username,
|
|
||||||
fields.phone,
|
|
||||||
fields.photo_unique_id,
|
|
||||||
fields.is_deleted_account,
|
|
||||||
raw,
|
|
||||||
)
|
|
||||||
async with pool.acquire() as conn, conn.transaction():
|
async with pool.acquire() as conn, conn.transaction():
|
||||||
await conn.execute(_UPSERT_PEER, account_id, peer_id, *args)
|
await conn.execute(
|
||||||
if history:
|
_UPSERT_PEER,
|
||||||
await conn.execute(_INSERT_HISTORY, account_id, peer_id, *args)
|
account_id,
|
||||||
|
peer_id,
|
||||||
|
fields.first_name,
|
||||||
|
fields.last_name,
|
||||||
|
fields.username,
|
||||||
|
fields.phone,
|
||||||
|
fields.photo_unique_id,
|
||||||
|
fields.is_deleted_account,
|
||||||
|
raw,
|
||||||
|
sorted(fields.unknown),
|
||||||
|
)
|
||||||
|
if not history:
|
||||||
|
return False
|
||||||
|
status = await conn.execute(_INSERT_CHANGE, account_id, peer_id)
|
||||||
|
return status == "INSERT 0 1"
|
||||||
Reference in new issue
Block a user