- GLOBAL_PRESENCE_AGGREGATOR_SPEC.md documentation - matrix-presence-aggregator service (Python/FastAPI) - Matrix sync loop for presence/typing - NATS publishing for room presence - city-service: presence_gateway for WS broadcast - Frontend: real-time online count in room list - useGlobalPresence hook - Live typing indicators - Active room highlighting
10 KiB
10 KiB
GLOBAL PRESENCE AGGREGATOR — DAARION.city
Version: 1.0.0 Location: docs/realtime/GLOBAL_PRESENCE_AGGREGATOR_SPEC.md
0. PURPOSE
Зробити єдиний центр правди про присутність (presence) та активність у місті:
- збирати Matrix presence/typing/room-activity на сервері,
- агрегувати їх на рівні кімнат (
city_room), - публікувати у NATS як події,
- транслювати у фронтенд через WebSocket з
city-service.
Результат: DAARION має "живе місто":
- список кімнат
/cityпоказує:- скільки людей онлайн,
- активність у реальному часі,
- майбутня City Map (2D/2.5D) живиться цими даними.
1. ARCHITECTURE OVERVIEW
┌─────────────────────────────────────────────────────────────────────────┐
│ DAARION PRESENCE SYSTEM │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐ │
│ │ Matrix │────▶│ matrix-presence- │────▶│ NATS │ │
│ │ Synapse │ │ aggregator │ │ JetStream │ │
│ │ │ │ (sync loop) │ │ │ │
│ └─────────────┘ └──────────────────────┘ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐ │
│ │ Browser │◀────│ city-service │◀────│ NATS Sub │ │
│ │ (WS) │ │ /ws/city/presence │ │ │ │
│ └─────────────┘ └──────────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Компоненти
-
matrix-presence-aggregator (новий сервіс)
- читає Matrix sync (presence, typing, room activity),
- тримає у пам'яті поточний стан присутності,
- публікує агреговані події в NATS.
-
NATS JetStream
- канал для presence/events:
city.presence.room.*city.presence.user.*
- канал для presence/events:
-
city-service (розширення)
- підписується на NATS події,
- тримає WebSocket з'єднання з фронтендом,
- пушить presence/room-activity у браузер.
-
web (Next.js UI)
- сторінка
/city:- показує
N onlineпо кожній кімнаті, - highlight "active" кімнати.
- показує
- сторінка
2. MATRIX SIDE — ЗВІДКИ БРАТИ ПОДІЇ
2.1. Окремий Matrix-юзер для агрегації
Спец-акаунт:
@presence_daemon:daarion.space- права:
- читати presence/typing у всіх
city_*кімнатах, - бути учасником цих кімнат.
- читати presence/typing у всіх
2.2. Sync-loop на сервері
Сервіс matrix-presence-aggregator:
- використовує
/syncMatrix (як клієнт), - фільтр:
{
"presence": {
"types": ["m.presence"]
},
"room": {
"timeline": { "limit": 0 },
"state": { "limit": 0 },
"ephemeral": {
"types": ["m.typing", "m.receipt"]
}
}
}
- робить long-polling з
since+timeout, - парсить:
presence.events→m.presence,rooms.join[roomId].ephemeral.events→m.typing,m.receipt.
3. DATA MODEL (IN-MEMORY AGGREGATOR)
3.1. Room presence state
from dataclasses import dataclass
from typing import Dict, List, Set, Optional
from datetime import datetime
@dataclass
class UserPresence:
user_id: str # "@user:domain"
status: str # "online" | "offline" | "unavailable"
last_active_ts: float # timestamp
@dataclass
class RoomPresence:
room_id: str # "!....:daarion.space"
alias: Optional[str] # "#city_energy:daarion.space"
city_room_slug: Optional[str] # "energy"
online_count: int
typing_user_ids: List[str]
last_event_ts: float
class PresenceState:
users: Dict[str, UserPresence]
rooms: Dict[str, RoomPresence]
room_members: Dict[str, Set[str]] # room_id -> set of user_ids
3.2. Мапінг Room → City Room
matrix-presence-aggregator має знати matrix_room_id ↔ city_room.slug.
Pull-mode (MVP):
- при старті сервісу:
GET /internal/city/rooms- зчитати всі
matrix_room_id/matrix_room_alias/slug, - зібрати мапу
roomId → slug.
- періодично (кожні 5 хвилин) оновлювати.
4. NATS EVENTS
4.1. Room-level presence
Subject:
city.presence.room.<slug>
Event payload:
{
"type": "room.presence",
"room_slug": "energy",
"matrix_room_id": "!gykdLyazhkcSZGHmbG:daarion.space",
"matrix_room_alias": "#city_energy:daarion.space",
"online_count": 5,
"typing_count": 1,
"typing_users": ["@user1:daarion.space"],
"last_event_ts": 1732610000000
}
4.2. User-level presence (опційний)
Subject:
city.presence.user.<localpart>
Payload:
{
"type": "user.presence",
"matrix_user_id": "@user1:daarion.space",
"status": "online",
"last_active_ts": 1732610000000
}
5. EVENT GENERATION LOGIC
5.1. Обробка m.presence
При кожному m.presence:
- оновити
PresenceState.users[userId], - для всіх кімнат, де є цей юзер — перерахувати
onlineCount, - якщо
onlineCountзмінився — публікувати нову подію.
5.2. Обробка m.typing
При m.typing:
content.user_ids→ список typing у кімнаті.- Зберегти в
RoomPresence.typing_user_ids. - Згенерувати івент
city.presence.room.<slug>.
5.3. Throttling
- подію публікувати тільки якщо
onlineCountзмінився, - або не частіше ніж раз на 3 секунди на кімнату.
6. CITY REALTIME GATEWAY (WEBSOCKET)
6.1. WebSocket endpoint
GET /ws/city/presence
Auth: JWT токен у query param або header.
6.2. Формат повідомлень
Snapshot (при підключенні):
{
"type": "snapshot",
"rooms": [
{ "room_slug": "general", "online_count": 3, "typing_count": 0 },
{ "room_slug": "welcome", "online_count": 1, "typing_count": 0 }
]
}
Incremental update:
{
"type": "room.presence",
"room_slug": "energy",
"online_count": 5,
"typing_count": 1
}
7. FRONTEND INTEGRATION
7.1. Список кімнат /city
State:
type RoomPresenceUI = {
onlineCount: number;
typingCount: number;
};
const [presenceBySlug, setPresenceBySlug] = useState<Record<string, RoomPresenceUI>>({});
WebSocket handler:
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'snapshot') {
const presence: Record<string, RoomPresenceUI> = {};
data.rooms.forEach(r => {
presence[r.room_slug] = {
onlineCount: r.online_count,
typingCount: r.typing_count
};
});
setPresenceBySlug(presence);
}
if (data.type === 'room.presence') {
setPresenceBySlug(prev => ({
...prev,
[data.room_slug]: {
onlineCount: data.online_count,
typingCount: data.typing_count
}
}));
}
};
7.2. UI
- Room card:
X online, typing badge - Active room: glow effect
- Typing animation
8. CONFIG / ENV
matrix-presence-aggregator
MATRIX_HS_URL=https://app.daarion.space
MATRIX_ACCESS_TOKEN=<presence_daemon_token>
MATRIX_USER_ID=@presence_daemon:daarion.space
CITY_SERVICE_INTERNAL_URL=http://city-service:7001
NATS_URL=nats://nats:4222
ROOM_PRESENCE_THROTTLE_MS=3000
city-service (realtime gateway)
NATS_URL=nats://nats:4222
JWT_SECRET=<secret>
9. ACCEPTANCE CRITERIA
- matrix-presence-aggregator запущений і синхронізується з Matrix
- NATS отримує події
city.presence.room.* - city-service має endpoint
/ws/city/presence - При підключенні WS клієнт отримує snapshot
- При зміні presence клієнт отримує update
- UI
/cityпоказує online count для кожної кімнати - Typing indicator відображається
10. FUTURE ENHANCEMENTS
- Agent presence — окремі статуси для AI-агентів
- City Map — візуалізація presence на 2D карті
- Push notifications — сповіщення про активність
- Historical analytics — статистика активності