mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Add all specification files (CHATSERVER, PROTOCOL, SCHEMA, API, SETUP), Claude Code config, and skill definitions for the OwnCord chat platform.
7.1 KiB
7.1 KiB
WebSocket Protocol Spec
All client-server communication (except file uploads and admin panel) happens over a single WebSocket connection. Messages are JSON with a type and payload.
Message Format
{
"type": "message_type",
"id": "unique-request-id",
"payload": { }
}
type— string, required. Determines how payload is interpreted.id— string, optional. Client-generated UUID for request/response correlation.payload— object, required. Contents vary by type.
Server responses to client requests include the same id for correlation.
Authentication
Client → Server
{ "type": "auth", "payload": { "token": "session-token-here" } }
Server → Client (success)
{ "type": "auth_ok", "payload": { "user": { "id": 1, "username": "alex", "avatar": "uuid.png", "role": "admin" }, "server_name": "My Server", "motd": "Welcome!" } }
Server → Client (failure)
{ "type": "auth_error", "payload": { "message": "Invalid or expired token" } }
Connection is closed by server after auth_error.
Chat Messages
Send Message (Client → Server)
{ "type": "chat_send", "id": "req-uuid", "payload": { "channel_id": 5, "content": "Hello everyone!", "reply_to": null, "attachments": ["upload-uuid-1"] } }
Message Broadcast (Server → Client)
{ "type": "chat_message", "payload": { "id": 1042, "channel_id": 5, "user": { "id": 1, "username": "alex", "avatar": "uuid.png" }, "content": "Hello everyone!", "reply_to": null, "attachments": [{ "id": "upload-uuid-1", "filename": "photo.jpg", "size": 204800, "mime": "image/jpeg", "url": "/files/upload-uuid-1" }], "timestamp": "2026-03-14T10:30:00Z" } }
Send Ack (Server → Client)
{ "type": "chat_send_ok", "id": "req-uuid", "payload": { "message_id": 1042, "timestamp": "2026-03-14T10:30:00Z" } }
Edit Message (Client → Server)
{ "type": "chat_edit", "id": "req-uuid", "payload": { "message_id": 1042, "content": "Hello everyone! (edited)" } }
Edit Broadcast (Server → Client)
{ "type": "chat_edited", "payload": { "message_id": 1042, "channel_id": 5, "content": "Hello everyone! (edited)", "edited_at": "2026-03-14T10:31:00Z" } }
Delete Message (Client → Server)
{ "type": "chat_delete", "id": "req-uuid", "payload": { "message_id": 1042 } }
Delete Broadcast (Server → Client)
{ "type": "chat_deleted", "payload": { "message_id": 1042, "channel_id": 5 } }
Reaction Add/Remove (Client → Server)
{ "type": "reaction_add", "payload": { "message_id": 1042, "emoji": "👍" } }
{ "type": "reaction_remove", "payload": { "message_id": 1042, "emoji": "👍" } }
Reaction Broadcast (Server → Client)
{ "type": "reaction_update", "payload": { "message_id": 1042, "channel_id": 5, "emoji": "👍", "user_id": 1, "action": "add" } }
Typing Indicators
Client → Server (throttle to 1 per 3 seconds)
{ "type": "typing_start", "payload": { "channel_id": 5 } }
Server → Client (broadcast to channel members)
{ "type": "typing", "payload": { "channel_id": 5, "user_id": 1, "username": "alex" } }
Client-side: show indicator for 5 seconds, reset on new typing event from same user.
Presence
Client → Server
{ "type": "presence_update", "payload": { "status": "online" } }
Status values: online, idle, dnd, offline
Server → Client (broadcast)
{ "type": "presence", "payload": { "user_id": 1, "status": "online" } }
Server auto-sets idle after 10 minutes of no WebSocket activity.
Channel Updates
Server → Client (on channel created/edited/deleted/reordered)
{ "type": "channel_create", "payload": { "id": 8, "name": "gaming", "type": "text", "category": "Hangout", "position": 3 } }
{ "type": "channel_update", "payload": { "id": 8, "name": "gaming-talk", "position": 4 } }
{ "type": "channel_delete", "payload": { "id": 8 } }
Channel types: text, voice, announcement
Voice Signaling
Join Voice Channel (Client → Server)
{ "type": "voice_join", "payload": { "channel_id": 10 } }
Server → Client (voice state updates, broadcast to channel)
{ "type": "voice_state", "payload": { "channel_id": 10, "user_id": 1, "username": "alex", "muted": false, "deafened": false, "speaking": false } }
Voice User Left (Server → Client)
{ "type": "voice_leave", "payload": { "channel_id": 10, "user_id": 1 } }
WebRTC Signaling (bidirectional)
{ "type": "voice_offer", "payload": { "channel_id": 10, "sdp": "..." } }
{ "type": "voice_answer", "payload": { "channel_id": 10, "sdp": "..." } }
{ "type": "voice_ice", "payload": { "channel_id": 10, "candidate": "..." } }
Voice Control (Client → Server)
{ "type": "voice_mute", "payload": { "muted": true } }
{ "type": "voice_deafen", "payload": { "deafened": true } }
Soundboard (Client → Server)
{ "type": "soundboard_play", "payload": { "sound_id": "uuid" } }
Member Updates
Server → Client
{ "type": "member_join", "payload": { "user": { "id": 5, "username": "newuser", "avatar": null, "role": "member" } } }
{ "type": "member_leave", "payload": { "user_id": 5 } }
{ "type": "member_update", "payload": { "user_id": 5, "role": "moderator" } }
{ "type": "member_ban", "payload": { "user_id": 5 } }
Initial State (sent after auth_ok)
Server → Client
{
"type": "ready",
"payload": {
"channels": [
{ "id": 1, "name": "general", "type": "text", "category": "Main", "position": 0, "unread_count": 3, "last_message_id": 1040 },
{ "id": 10, "name": "voice-chat", "type": "voice", "category": "Main", "position": 1 }
],
"members": [
{ "id": 1, "username": "alex", "avatar": "uuid.png", "role": "admin", "status": "online" },
{ "id": 2, "username": "jordan", "avatar": null, "role": "member", "status": "idle" }
],
"voice_states": [
{ "channel_id": 10, "user_id": 2, "muted": false, "deafened": false }
],
"roles": [
{ "id": 1, "name": "Owner", "color": "#E74C3C", "permissions": 2147483647 },
{ "id": 2, "name": "Admin", "color": "#F39C12", "permissions": 1073741823 },
{ "id": 3, "name": "Member", "color": null, "permissions": 1049601 }
]
}
}
Message History (REST, not WebSocket)
Fetched via REST API, not WebSocket, to keep the WS connection lean.
GET /api/channels/{id}/messages?before={message_id}&limit=50
Error Format
Any request that fails returns:
{ "type": "error", "id": "original-req-uuid", "payload": { "code": "FORBIDDEN", "message": "You don't have permission to post in this channel" } }
Error codes: FORBIDDEN, NOT_FOUND, RATE_LIMITED, INVALID_INPUT, SERVER_ERROR
Rate Limits
- Chat messages: 10/sec per user
- Typing events: 1/3sec per user per channel
- Presence updates: 1/10sec per user
- Reactions: 5/sec per user
- Voice signaling: 20/sec per user
- Soundboard: 1/3sec per user
Server sends rate_limited error with retry_after in seconds.