OmniStream Docs
  • Panduan Pengguna
  • Developer
  • API Reference
Developer Hub
Pendahuluan
Autentikasi
Model Data
Webhook
WebSocket
    Koneksi WebSocketEvent routing WebSocketPesan klien ke server
Self-Hosting
Error & Rate Limit
WebSocket

Event routing WebSocket

Event routing WebSocket

ws-server bertindak sebagai jembatan antara Redis pub/sub dan klien browser. Ia berlangganan empat channel Redis dan meneruskan setiap pesan ke koneksi yang relevan dengan aturan routing yang bervariasi per channel.

Halaman ini mendokumentasikan channel mana yang dilangganan, cara routing per peran, dan skema JSON setiap event.

Channel Redis

Subscriber di crates/ws-server/src/subscriber.rs mengikat diri pada empat channel:

Redis channelDipublikasikan olehRouting
conversation_updateschat-engine-*, api-gatewayAdmin/supervisor dapat semua; agent hanya yang di-assign + percakapan baru
typing_indicatorsapi-gateway (saat agent mengetik)Broadcast ke semua koneksi
agent_presencews-server (saat agent online/offline)Broadcast ke semua koneksi
sla_breachesapi-gateway (scheduler)Assigned agent + semua supervisor/admin

Kode referensi: subscriber.rs::run, subscriber.rs::dispatch.

Envelope pesan

Setiap pesan yang diterima klien dibungkus dengan struktur:

Code
{ "channel": "conversation_updates", "data": { /* event payload */ } }

channel mengidentifikasi jenis event; data berisi payload spesifik. Frontend sebaiknya men-switch berdasarkan channel untuk menentukan handler.

conversation_updates

Channel utama untuk pembaruan percakapan. Payload adalah ConversationUpdateEvent yang diterbitkan oleh chat-engine (saat pesan masuk) atau api-gateway (saat pesan keluar, resolve, transfer).

Routing

Code
ConversationUpdateEvent │ ├─ ConversationCreated (unassigned) │ → broadcast_to_all (supaya semua agent bisa mengklaim) │ ├─ unassigned assigned_agent_id (conversation tanpa owner) │ → broadcast_to_all │ └─ assigned assigned_agent_id = X → send_to_agent(X) (agent pemilik) → broadcast_to_supervisors_except(X) (admin/supervisor kecuali X sendiri)

Efek praktis:

  • Agent hanya menerima event untuk percakapan yang memang miliknya.
  • Admin dan supervisor menerima event untuk semua percakapan, termasuk yang belum di-assign.
  • Agent yang belum memiliki percakapan apa pun tetap menerima event ConversationCreated dari percakapan yang baru muncul sehingga UI dapat menampilkan notifikasi "percakapan baru".

Variants ConversationUpdateEvent

Enum sumbernya ada di crates/omni-common/src/models/events.rs:

Code
pub enum ConversationUpdateEvent { MessageReceived { ... }, MessageSent { ... }, MessageStatus { ... }, ConversationCreated { ... }, ConversationResolved { ... }, ConversationAssigned { ... }, }

Mapping ke webhook keluar dibahas di Outgoing webhook.

Contoh payload MessageReceived

Code
{ "channel": "conversation_updates", "data": { "type": "MessageReceived", "message_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "conversation_id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "contact_id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", "channel_name": "whatsapp", "sender_name": "Budi", "content": "Halo, butuh bantuan invoice", "assigned_agent_id": null, "timestamp": "2026-04-11T10:20:00Z" } }

Contoh payload ConversationAssigned

Code
{ "channel": "conversation_updates", "data": { "type": "ConversationAssigned", "conversation_id": "b2c3d4e5-...", "assigned_agent_id": "f6a7b8c9-...", "previous_agent_id": null, "timestamp": "2026-04-11T10:22:00Z" } }

typing_indicators

Dikirim ketika agent memulai atau berhenti mengetik pada percakapan tertentu. Dipublikasikan oleh api-gateway saat menerima pesan WebSocket {"type":"typing", ...} dari klien (lihat Client messages).

Routing

Broadcast ke semua koneksi, tanpa filter peran. Frontend bertanggung jawab menyaring: hanya menampilkan indikator pada percakapan yang sedang dibuka user.

Code
// subscriber.rs RedisChannel::TypingIndicators => broadcast_to_all(&registry, &msg).await,

Payload

Code
{ "channel": "typing_indicators", "data": { "conversation_id": "b2c3d4e5-...", "agent_id": "f6a7b8c9-...", "agent_name": "Sari", "is_typing": true, "timestamp": "2026-04-11T10:23:00Z" } }

agent_presence

Dikirim ketika agent connect (pertama kali) atau disconnect (koneksi terakhir). ws-server sendiri yang mempublikasikannya berdasarkan state registry internal — tidak ada proses eksternal yang mengetahui presence selain ws-server.

Routing

Broadcast ke semua koneksi. Frontend memakainya untuk menampilkan badge online/offline di daftar agent.

Payload

Code
{ "channel": "agent_presence", "data": { "agent_id": "f6a7b8c9-...", "status": "online", "timestamp": "2026-04-11T10:20:00Z" } }

Nilai status: "online" saat koneksi pertama, "offline" saat koneksi terakhir tutup.

sla_breaches

Dikirim oleh scheduler di api-gateway ketika SLA policy dilanggar (mis. percakapan tidak dibalas dalam waktu yang ditentukan). Berguna untuk notifikasi real-time ke supervisor.

Routing

  • Assigned agent pemilik percakapan mendapat event
  • Semua admin dan supervisor juga mendapat event (broadcast_to_supervisors)
  • Agent lain tidak mendapat event
Code
RedisChannel::SlaBreaches => { if let Some(agent_id) = event.assigned_agent_id { send_to_agent(&registry, agent_id, &msg).await; } broadcast_to_supervisors_except(&registry, Uuid::nil(), &msg).await; }

Payload

Code
{ "channel": "sla_breaches", "data": { "breach_id": "d1e2f3a4-...", "conversation_id": "b2c3d4e5-...", "policy_id": "e5f6a7b8-...", "policy_name": "First response < 5 min", "assigned_agent_id": "f6a7b8c9-...", "breach_type": "first_response", "threshold_seconds": 300, "elapsed_seconds": 612, "timestamp": "2026-04-11T10:30:00Z" } }

Pemicu dan prioritas SLA policy dijelaskan di crates/api-gateway/src/scheduler.rs::find_policy.

Matriks routing ringkas

ChannelAdminSupervisorAgent (pemilik)Agent (bukan pemilik)
conversation_updates (assigned)✔✔✔✘
conversation_updates (unassigned / created)✔✔✔✔
typing_indicators✔✔✔✔
agent_presence✔✔✔✔
sla_breaches✔✔✔✘

✔ diterima, ✘ tidak diteruskan oleh server.

Idempotency di frontend

Karena klien dapat memiliki banyak koneksi (multi-tab) dan reconnect dapat menyebabkan duplikasi ringan, frontend harus idempoten:

  • Pesan — dedup berdasarkan message_id.
  • Conversation update — dedup berdasarkan (conversation_id, timestamp) atau state-last-wins.
  • Presence — simpan nilai terakhir saja; event lama tidak relevan.

Troubleshooting

  • Agent tidak menerima event untuk percakapan yang di-assign — pastikan assigned_agent_id di DB benar-benar terisi dengan UUID agent yang sedang login. Tanpa assignment yang valid, filter server tidak meneruskan event.
  • Supervisor tidak menerima typing indicator agent lain — typing indicators di-broadcast ke semua; jika tidak terlihat, frontend mungkin menyaring berdasarkan percakapan yang sedang dibuka. Buka percakapan tersebut untuk memverifikasi.
  • Presence online tidak muncul — hanya dipublikasikan saat koneksi pertama agent muncul. Jika agent sudah memiliki tab lain yang aktif, tab baru tidak menghasilkan event presence.
  • SLA breach event tidak sampai ke supervisor — periksa scheduler api-gateway; ia adalah satu-satunya publisher ke sla_breaches. Event hanya diterbitkan ketika policy cocok — lihat prioritas matching di CLAUDE.md.

File terkait

  • Subscriber: crates/ws-server/src/subscriber.rs
  • Handler registry: crates/ws-server/src/handler.rs
  • Event types: crates/omni-common/src/models/events.rs
  • Publisher (inbound): crates/chat-engine/src/main.rs
  • Publisher (outbound + SLA): crates/api-gateway/src/scheduler.rs, crates/api-gateway/src/routes/messages/mod.rs
Last modified on June 8, 2026
Koneksi WebSocketPesan klien ke server
On this page
  • Channel Redis
  • Envelope pesan
  • conversation_updates
    • Routing
    • Variants ConversationUpdateEvent
    • Contoh payload MessageReceived
    • Contoh payload ConversationAssigned
  • typing_indicators
    • Routing
    • Payload
  • agent_presence
    • Routing
    • Payload
  • sla_breaches
    • Routing
    • Payload
  • Matriks routing ringkas
  • Idempotency di frontend
  • Troubleshooting
  • File terkait
JSON
Rust
JSON
JSON
Rust
JSON
JSON
Rust
JSON