From f62c345e2b7a0408681a7e2114a3a8cbc4a8542b Mon Sep 17 00:00:00 2001 From: h Date: Mon, 19 Oct 2026 03:21:57 +0000 Subject: [PATCH] feat(backend): who of the given people is online now in one request --- backend/src/api/routers/presence.py | 11 +++++++++++ backend/src/utils/read/presence.py | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/backend/src/api/routers/presence.py b/backend/src/api/routers/presence.py index 51b40bd..6ebfa21 100644 --- a/backend/src/api/routers/presence.py +++ b/backend/src/api/routers/presence.py @@ -9,6 +9,7 @@ from api.timezone import checked_tz from utils.read import presence, presence_stats from utils.read.models import ( DEFAULT_LIMIT, + MAX_LIMIT, Page, PresenceHourly, PresenceSample, @@ -54,6 +55,16 @@ async def current_presence( return await presence.current_presence(pool, account_id, peer_id) +@router.get("/online") +async def online_now( + pool: FromDishka[asyncpg.Pool], + account_id: AccountId, + peer_ids: Annotated[str, Query()], +) -> list[PresenceSample]: + parsed = [int(part) for part in peer_ids.split(",") if part.strip()] + return await presence.online_now(pool, account_id, parsed[:MAX_LIMIT]) + + @router.get("/hourly") async def presence_hourly( pool: FromDishka[asyncpg.Pool], diff --git a/backend/src/utils/read/presence.py b/backend/src/utils/read/presence.py index db1351e..41ca123 100644 --- a/backend/src/utils/read/presence.py +++ b/backend/src/utils/read/presence.py @@ -48,6 +48,29 @@ async def current_presence( return PresenceSample(**dict(row)) if row is not None else None +_ONLINE = """ +SELECT peer_id, ts, status, last_online_date, next_offline_date +FROM ( + SELECT DISTINCT ON (peer_id) + peer_id, ts, status, last_online_date, next_offline_date + FROM presence + WHERE account_id = $1 AND peer_id = ANY($2::bigint[]) + AND ts > now() - interval '1 hour' + ORDER BY peer_id, ts DESC +) latest +WHERE status = 'online' AND next_offline_date > now() +""" + + +async def online_now( + pool: asyncpg.Pool, account_id: int, peer_ids: list[int] +) -> list[PresenceSample]: + if not peer_ids: + return [] + rows = await pool.fetch(_ONLINE, account_id, peer_ids) + return [PresenceSample(**dict(row)) for row in rows] + + async def presence_hourly( pool: asyncpg.Pool, account_id: int,