mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""In-memory user presence derived from WebSocket connections only."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from fastapi import WebSocket
|
|
|
|
|
|
class PresenceService:
|
|
def __init__(self) -> None:
|
|
self._connections: dict[int, set[WebSocket]] = {}
|
|
self._last_seen: dict[int, datetime] = {}
|
|
|
|
def register_connection(self, user_id: int, websocket: WebSocket) -> bool:
|
|
"""Track a live connection. Returns True if the user became online."""
|
|
connections = self._connections.setdefault(user_id, set())
|
|
was_online = bool(connections)
|
|
connections.add(websocket)
|
|
return not was_online
|
|
|
|
def unregister_connection(self, user_id: int, websocket: WebSocket) -> tuple[bool, datetime | None]:
|
|
"""Remove a connection. Returns (became_offline, last_seen) when the last conn drops."""
|
|
connections = self._connections.get(user_id)
|
|
if not connections:
|
|
return False, self._last_seen.get(user_id)
|
|
|
|
connections.discard(websocket)
|
|
if connections:
|
|
return False, None
|
|
|
|
del self._connections[user_id]
|
|
last_seen = datetime.now()
|
|
self._last_seen[user_id] = last_seen
|
|
return True, last_seen
|
|
|
|
def touch(self, user_id: int) -> None:
|
|
"""Refresh activity timestamp while online."""
|
|
if self.is_online(user_id):
|
|
self._last_seen[user_id] = datetime.now()
|
|
|
|
def is_online(self, user_id: int) -> bool:
|
|
connections = self._connections.get(user_id)
|
|
return bool(connections)
|
|
|
|
def get_last_seen(self, user_id: int) -> datetime | None:
|
|
if self.is_online(user_id):
|
|
return self._last_seen.get(user_id) or datetime.now()
|
|
return self._last_seen.get(user_id)
|
|
|
|
def get_presence(self, user_id: int) -> tuple[bool, datetime | None]:
|
|
online = self.is_online(user_id)
|
|
if online:
|
|
return True, self.get_last_seen(user_id)
|
|
last_seen = self._last_seen.get(user_id)
|
|
return False, last_seen
|
|
|
|
def remove_user(self, user_id: int) -> None:
|
|
"""Drop all presence state for a deleted user."""
|
|
self._connections.pop(user_id, None)
|
|
self._last_seen.pop(user_id, None)
|
|
|
|
|
|
presence_service = PresenceService()
|