Websockets are a crucial part of any real-time feature. On our case, we're talking about Chat and Notifications.
WebSockets Connection Test
This guide explains how to test WebSockets connection with Circle's real-time messaging system using the ws
library.
Basic Setup
Create a WebSocket connection to Circle's server:
Copy const WebSocket = require("ws");
const channelName = "ChatRoomChannel"; // Use required channel name
const socket = new WebSocket("wss://app.circle.so/cable", {
headers: {
Origin: "https://your-whitelisted-domain.com",
Authorization: "Bearer HEADLESS_MEMBER_ACCESS_TOKEN"
}
});
Event Handling
1. Connection Open
Handle the connection establishment and channel subscription:
Copy socket.on("open", function open() {
console.log("WebSocket connection opened.");
// Subscribe to the chat channel
const subscribeMessage = JSON.stringify({
command: "subscribe",
identifier: JSON.stringify({
channel: channelName,
community_member_id: COMMUNITY_MEMBER_ID
})
});
// Send subscription request
socket.send(subscribeMessage);
// Optional: Send a test message
socket.send(JSON.stringify({
action: "message",
data: "Hello Server!"
}));
});
2. Receiving Messages
Handle incoming messages from the server:
Copy socket.on("message", function incoming(data) {
const message = JSON.parse(data);
if (message.channel === channelName) {
console.log(`Message from channel ${channelName}:`, message.data);
} else {
console.log("Message from server:", message);
}
});
3. Error Handling
Implement error handling for the WebSocket connection:
Copy socket.on("error", function error(err) {
console.error("WebSocket error:", err);
});
4. Connection Close
Handle connection closure:
Copy socket.on("close", function close() {
console.log("WebSocket connection closed.");
});
Complete Implementation
Here's the complete code combining all the components:
Copy const WebSocket = require("ws");
const channelName = "ChatRoomChannel";
function initializeWebSocket() {
const socket = new WebSocket("wss://app.circle.so/cable", {
headers: {
Origin: "https://your-whitelisted-domain.com",
Authorization: "Bearer HEADLESS_MEMBER_ACCESS_TOKEN"
}
});
// Handle connection open
socket.on("open", function open() {
console.log("WebSocket connection opened.");
// Subscribe to channel
const subscribeMessage = JSON.stringify({
command: "subscribe",
identifier: JSON.stringify({
channel: channelName,
community_member_id: COMMUNITY_MEMBER_ID
})
});
socket.send(subscribeMessage);
});
// Handle incoming messages
socket.on("message", function incoming(data) {
const message = JSON.parse(data);
if (message.channel === channelName) {
console.log(`Message from channel ${channelName}:`, message.data);
} else {
console.log("Message from server:", message);
}
});
// Handle errors
socket.on("error", function error(err) {
console.error("WebSocket error:", err);
});
// Handle connection close
socket.on("close", function close() {
console.log("WebSocket connection closed.");
// Optional: Implement reconnection logic here
});
return socket;
}
// Initialize the WebSocket connection
const socket = initializeWebSocket();
Run Test
Copy $ node websocket-connection-test.js
On successful connection, you will start receiving ping
from server.
Example:
Copy WebSocket connection opened.
Message from server: { type: 'welcome', sid: 'JnehncGjAQuLBIBoImSMk' }
Message from server: {
identifier: '{"channel":"ChatRoomChannel","community_member_id": COMMUNITY_MEMBER_ID}',
type: 'confirm_subscription'
}
Message from server: { type: 'ping', message: 1729855056 }
Message from server: { type: 'ping', message: 1729855059 }
Message from server: { type: 'ping', message: 1729855062 }
Notifications
Name: *NotificationChannel*
Pubsub queue: "notification-channel-#{ community_member_id }"
This channel is used for communication of all real time notifications for a community member. This channel receives following events:
newNotification
This event is received whenever community member receives a new in-app notification
updateNewNotificationCount
This event is received whenever there is change in community member’s in-app notification count
Additional information received with the event:
Copy new_notifications_count: number
resetNewNotificationCount
This event is received to mark all notifications as read.
Additional information received with the event:
Copy new_notifications_count: 0
Chat
We have 3 channels to manage websocket communications for messaging.
Channel details:
Name: *ChatRoomChannel*
Pubsub queue: chat-room-channel-#{community_member.id}
Events:
Chat Room events:
chatRoomCreated
This event is broadcasted on a non embedded chat room creation.
Additional information received with the event
Copy interface ChatRoom {
id: number;
uuid: string;
identifier: string;
unread_messages_count: number;
chat_room_kind: string;
chat_room_name: string;
chat_room_description: string;
chat_room_show_history: boolean;
other_participants_preview: Record<string, any>[];
current_participant: Record<string, any>;
last_message: Record<string, any>;
}
chatRoomUpdated
This event gets broadcasted in following cases:
when non embedded chat room name is changed.
when non embedded chat room gets pinned.
Please note that same event is broadcasted on chat-room-#{chat_room.id}-channel
channel on updating room specific attributes like :pinned_message_id, :show_history, :description, :name
attributes.
Additional information received with the event:
Copy interface SimplifiedChatRoom {
uuid: string;
chat_room_name: string;
chat_room_description: string;
chat_room_show_history: boolean;
pinned_message: Record<string, any>;
}
chatRoomDeleted
This event gets broadcasted on chat room deletion.
Additional information received with the event:
Copy {
chat_room_uuid: string
}
chatRoomRead
This event gets broadcasted when chatroom gets marked as read by a participant.
Additional information received with the event:
Copy {
chat_room_uuid: string
}
chatRoomPinned
This event gets broadcasted when a non embedded chat room is pinned by a community member.
Additional information received with the event:
Copy {
chat_room_id: number
pinned_at: string
}
chatRoomUnPinned
This event gets broadcasted when a non embedded chat room is un-pinned by a community member.
Additional information received with the event:
Copy {
chat_room_id: number
}
ChatRoomMessage events:
newMessage
This event gets broadcasted for each new non reply message in a non embedded chat room.
Additional information received with the event:
Copy interface JSONMessage {
id: number;
chat_room_uuid: string;
chat_room_kind: string;
chat_room_participant_id: number; // Assuming this is a number based on previous examples
body: string;
rich_text_body: Record<string, any>;
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
chat_thread_replies_count: number;
}
chat-room-#{chat_room.id}-channel
We are using this channel to post chat room events which are not intended for a specific community member.
Channel details:
Name: *Chats*::*RoomChannel*
Pubsub queue: chat-room-#{*chat_room*.id}-channel
Events:
ChatRoomMessage events:
newMessage
This event gets broadcasted for each new message in the chat room
Additional information received with the event:
Copy interface JSONMessage {
id: number;
chat_room_uuid: string;
chat_room_kind: string;
chat_room_participant_id: number; // Assuming this is a number based on previous examples
body: string;
rich_text_body: Record<string, any>;
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
chat_thread_replies_count: number;
}
deletedMessage
This event gets broadcasted when a message gets deleted from the chat room
Additional information received with the event:
Copy interface ParentMessage {
id: number;
chat_room_uuid: string;
chat_room_participant_id: number; // Assuming this is a number based on previous examples
body: string;
rich_text_body: Record<string, any>;
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
}
interface MessageWithParent {
id: number;
parent_message: ParentMessage;
}
updatedMessage
This event gets broadcasted when a message is updated
Additional information received with the event:
Copy interface JSONMessage {
id: number;
chat_room_uuid: string;
chat_room_participant_id: number; // Assuming this is a number based on previous examples
body: string;
rich_text_body: Record<string, any>;
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
}
We are using this channel to communicate events for the threads that a community is part of
Channel details:
Name: *Chats*::*CommunityMemberThreadsChannel*
Pubsub queue: chat-community-member-#{community_member_id}-threads-channel
newMessage
This event gets broadcasted for each new message in the chat thread.
Additional information received with the event:
Copy interface JSONMessage {
id: number;
chat_room_uuid: string;
chat_room_kind: string;
chat_room_participant_id: number; // Assuming this is a number based on previous examples
body: string;
rich_text_body: Record<string, any>;
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
chat_thread_replies_count: number;
}
updatedMessage
This event gets broadcasted when a message is updated in chat thread
Additional information received with the event:
Copy interface ChatMessage {
id: number;
chat_room_uuid: string;
chat_room_participant_id: number; // Assuming this is a number based on the comment
body: string;
rich_text_body: Record<string, any>; // Using Record for a generic object type
sent_at: string;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
}
deletedMessage
This event gets broadcasted when a message gets deleted from the chat thread
Additional information received with the event:
Copy interface ParentMessage {
id: number;
chat_room_participant_id: string;
rich_text_body: Record<string, any>;
created_at: string;
sender: Record<string, any>;
creation_uuid: string;
chat_thread_id: number;
parent_message_id: number;
lesson_id: number;
edited_at: string;
replies_count: number;
total_thread_participants_count: number;
thread_participants_preview: Record<string, any>;
}
interface MessageWithParent {
id: number;
parent_message: ParentMessage;
}
chatThreadRead
This event gets broadcasted when chatroom gets marked as read by a participant.
Additional information received with the event:
Copy {
chat_thread_id: number
}
Last updated 4 months ago