WebSocket API
Real-time ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΡ Π΄Π»Ρ ΡΠ°ΡΠΎΠ² ΠΈ Π³ΠΎΠ»ΠΎΡΠΎΠ²ΡΡ ΠΊΠ°Π½Π°Π»ΠΎΠ²
ΠΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊ WebSocket
WebSocket ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π»Ρ real-time ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ: Π½ΠΎΠ²ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ, Π³ΠΎΠ»ΠΎΡΠΎΠ²ΡΠ΅ Π·Π²ΠΎΠ½ΠΊΠΈ, ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ΅ΡΠ²Π΅ΡΠΎΠ² ΠΈ ΠΊΠ°Π½Π°Π»ΠΎΠ².
Endpoint
wss://api.enot.space/ws?token=YOUR_JWT_TOKEN
ΠΡΠΈΠΌΠ΅Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ (JavaScript)
const token = localStorage.getItem('token');
const ws = new WebSocket(`wss://api.enot.space/ws?token=${token}`);
ws.onopen = () => {
console.log('WebSocket connected');
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log('Received:', message);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('WebSocket disconnected');
};
ΠΠ°ΠΆΠ½ΠΎ: JWT ΡΠΎΠΊΠ΅Π½ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±ΡΡΡ Π²Π°Π»ΠΈΠ΄Π½ΡΠΌ. ΠΡΠΈ ΠΈΡΡΠ΅ΡΠ΅Π½ΠΈΠΈ ΡΠΎΠΊΠ΅Π½Π° WebSocket ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Π±ΡΠ΄Π΅Ρ Π·Π°ΠΊΡΡΡΠΎ.
Π’ΠΈΠΏΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ
ΠΡΠ΅ WebSocket ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΠΈΠΌΠ΅ΡΡ ΠΏΠΎΠ»Π΅ type, ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΡΡΠ΅Π΅ ΡΠΈΠΏ ΡΠΎΠ±ΡΡΠΈΡ.
| Π’ΠΈΠΏ | ΠΠ°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ | ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ |
|---|---|---|
ping |
Client β Server | ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΡ |
pong |
Server β Client | ΠΡΠ²Π΅Ρ Π½Π° ping |
subscribe |
Client β Server | ΠΠΎΠ΄ΠΏΠΈΡΠΊΠ° Π½Π° ΠΊΠ°Π½Π°Π» |
unsubscribe |
Client β Server | ΠΡΠΏΠΈΡΠΊΠ° ΠΎΡ ΠΊΠ°Π½Π°Π»Π° |
message_new |
Server β Client | ΠΠΎΠ²ΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Π² ΠΊΠ°Π½Π°Π»Π΅ |
message_update |
Server β Client | Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΎ |
message_delete |
Server β Client | Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΡΠ΄Π°Π»Π΅Π½ΠΎ |
voice_state_update |
Client β Server | ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π³ΠΎΠ»ΠΎΡΠΎΠ²ΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ |
voice_state_add |
Server β Client | ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠ»ΡΡ ΠΊ Π³ΠΎΠ»ΠΎΡΠΎΠ²ΠΎΠΌΡ ΠΊΠ°Π½Π°Π»Ρ |
voice_state_remove |
Server β Client | ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΎΡΠΊΠ»ΡΡΠΈΠ»ΡΡ ΠΎΡ Π³ΠΎΠ»ΠΎΡΠΎΠ²ΠΎΠ³ΠΎ ΠΊΠ°Π½Π°Π»Π° |
webrtc_signal |
Bidirectional | WebRTC signaling (offer, answer, ICE) |
ΠΠΎΠ΄ΠΏΠΈΡΠΊΠ° Π½Π° ΠΊΠ°Π½Π°Π»Ρ
Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠ°ΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΠΈΠ· ΠΊΠ°Π½Π°Π»Π°, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ΄ΠΏΠΈΡΠ°ΡΡΡΡ Π½Π° Π½Π΅Π³ΠΎ.
ΠΠΎΠ΄ΠΏΠΈΡΠΊΠ°
ws.send(JSON.stringify({
type: 'subscribe',
channel_id: 123
}));
ΠΡΠΏΠΈΡΠΊΠ°
ws.send(JSON.stringify({
type: 'unsubscribe',
channel_id: 123
}));
Π‘ΠΎΠ²Π΅Ρ: ΠΠΎΠ΄ΠΏΠΈΡΡΠ²Π°ΠΉΡΠ΅ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π½Π° Π°ΠΊΡΠΈΠ²Π½ΡΠ΅ ΠΊΠ°Π½Π°Π»Ρ Π΄Π»Ρ ΡΠΊΠΎΠ½ΠΎΠΌΠΈΠΈ ΡΡΠ°ΡΠΈΠΊΠ°.
Π‘ΠΎΠ±ΡΡΠΈΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ
ΠΠΎΠ²ΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅
{
"type": "message_new",
"data": {
"id": 456,
"channel_id": 123,
"user_id": 1,
"content": "ΠΡΠΈΠ²Π΅Ρ!",
"created_at": "2026-05-05T18:00:00Z",
"author": {
"id": 1,
"username": "user",
"display_name": "User#1234"
}
}
}
Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΎ
{
"type": "message_update",
"data": {
"id": 456,
"channel_id": 123,
"content": "ΠΡΠΈΠ²Π΅Ρ, ΠΌΠΈΡ!",
"edited_at": "2026-05-05T18:05:00Z"
}
}
Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΡΠ΄Π°Π»Π΅Π½ΠΎ
{
"type": "message_delete",
"data": {
"id": 456,
"channel_id": 123
}
}
ΠΠΎΠ»ΠΎΡΠΎΠ²ΡΠ΅ ΡΠΎΠ±ΡΡΠΈΡ
ΠΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊ Π³ΠΎΠ»ΠΎΡΠΎΠ²ΠΎΠΌΡ ΠΊΠ°Π½Π°Π»Ρ
ws.send(JSON.stringify({
type: 'voice_state_update',
channel_id: 123,
muted: false,
deafened: false,
screen_sharing: false
}));
ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠ»ΡΡ
{
"type": "voice_state_add",
"data": {
"user_id": 2,
"channel_id": 123,
"muted": false,
"deafened": false,
"screen_sharing": false,
"display_name": "User#5678"
}
}
WebRTC Signaling
// ΠΡΠΏΡΠ°Π²ΠΊΠ° offer
ws.send(JSON.stringify({
type: 'webrtc_signal',
to_user_id: 2,
signal_type: 'offer',
data: {
sdp: '...',
type: 'offer'
}
}));
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ answer
{
"type": "webrtc_signal",
"from_user_id": 2,
"signal_type": "answer",
"data": {
"sdp": "...",
"type": "answer"
}
}
Rate Limiting
WebSocket ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΡΠ°ΠΊΠΆΠ΅ ΠΏΠΎΠ΄Π²Π΅ΡΠΆΠ΅Π½Ρ rate limiting:
- 60-120 ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ Π² ΠΌΠΈΠ½ΡΡΡ Π½Π° ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
- ΠΡΠΈ ΠΏΡΠ΅Π²ΡΡΠ΅Π½ΠΈΠΈ Π»ΠΈΠΌΠΈΡΠ° ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Π±ΡΠ΄Π΅Ρ Π·Π°ΠΊΡΡΡΠΎ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ debounce Π΄Π»Ρ ΡΠ°ΡΡΡΡ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ
Best Practices
- Π Π΅Π°Π»ΠΈΠ·ΡΠΉΡΠ΅ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΠΏΠ΅ΡΠ΅ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Ρ exponential backoff
- ΠΡΠΏΡΠ°Π²Π»ΡΠΉΡΠ΅ ping ΠΊΠ°ΠΆΠ΄ΡΠ΅ 30 ΡΠ΅ΠΊΡΠ½Π΄ Π΄Π»Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠ°Π½ΠΈΡ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΡ
- ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΠΉΡΠ΅ Π²ΡΠ΅ ΡΠΈΠΏΡ ΡΠΎΠ±ΡΡΠΈΠΉ gracefully
- ΠΠΎΠ΄ΠΏΠΈΡΡΠ²Π°ΠΉΡΠ΅ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π½Π° Π°ΠΊΡΠΈΠ²Π½ΡΠ΅ ΠΊΠ°Π½Π°Π»Ρ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ Π½Π° ΠΊΠ»ΠΈΠ΅Π½ΡΠ΅
- Π₯ΡΠ°Π½ΠΈΡΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ Π΄Π»Ρ offline ΡΠ΅ΠΆΠΈΠΌΠ°