Sariska provides a Java API and easy-to-integrate SDKs for web, mobile, front-end, and cloud infrastructure to add real-time features in your applications.
Key Features:
Real-time messaging for in-app chats and instant messaging
Easy installation
Socket creation and management
Channel creation, joining, and leaving
Sending messages, poll votes, and message replies
Presence management (track: typing, joining and leaving users)
History management (fetching chat history and specific messages)
To disconnect the user from any previous WebSocket connections and tabs before opening a new one, set the disconnect_past_tabs parameter to true in the WebSocket connection.
Disconnect Socket
Close the WebSocket connection to the Sariska server. This will terminate all active channels and prevent further communication with the server.
// Close connection to the serversocket.disconnect();
Step 3 : Create Channel
Channels cannot be created directly; instead, they are created through the socket object by calling socket.channel(topic) with the topic of the channel. The topic is a string that identifies the channel, and it can be any string you like.
Channel Prefix
Each channel name starts with a prefix that determines the message serialization format and persistence behavior.
chat: Use this prefix for persisting messages to the database. This prefix requires a fixed schema and is ideal for chat applications.
channel =socket.channel("chat:room123");
rtc: Use this prefix for scenarios where message persistence is not necessary. This prefix allows sending arbitrary JSON data, making it suitable for events in multiplayer games, IoT applications, and others.
channel =socket.channel("rtc:room123");
sariska: Use this prefix for performance-critical applications that utilize Flatbuffers as the serialization format and do not require message persistence. This prefix provides zero-copy serialization/deserialization, significantly enhancing performance.
channel =socket.channel("sariska:room123");
Handle Errors
When an error occurs on the channel, the ERROR callback is triggered. The callback receives the error information in payload, if available.
When the channel is closed, the CLOSE callback is invoked. This signifies that the communication on the channel has ended and no further data can be exchanged.
To join a channel, call the join() method on the channel object. The join() method returns a promise that resolves when the client has successfully joined the channel. When sending data, you can utilize the .receive() hook to monitor the status of the outgoing push message.
// Join the channelchannel.join()// Ignore any messages with the "ignore" label.receive("ignore",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {// Log the received "ignore" messageLog.i("IGNORE"); }})// Upon receiving a message with the "ok" label, assume the join request was successful.receive("ok",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {// Log the join confirmation messageLog.i("JOINED with "+envelope.toString()); }});// Leave the Channelchannel.leave();
Channel New Message
channel.on("new_message",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {finalChatMessage message;System.out.println("NEW MESSAGE: "+envelope.toString());try { message =objectMapper.treeToValue(envelope.getPayload(),ChatMessage.class);// Retrieve the user ID from the messagemessage.getUserId();// Log the message details for informationLog.i(TAG,"MESSAGE: "+ message);addToList(message); } catch (JsonProcessingException e) {// Log the errorLog.e(TAG,"Unable to parse message", e); } }});
Payload of message containing message details, including:
content: The message content
id: The sender's user ID
name: The sender's name
content_type: The message content type (media, emoji, text)
is_delivered: Whether the message has been delivered to the recipient
created_by: The ID of the user who sent the message
created_by_name: The name of the user who sent the message
timestamp: The message timestamp
Send Message
Once you've established a connection to a channel, you can start sending messages to other connected clients. To send a message, use the push() method on the channel object.
// Create a new HashMap to store the message payloadMap<String,Object> payload =newHashMap<>();// Add the message content to the payloadpayload.put("hi, from nick", content);// Send the message to the channelchannel.push("new_message", payload)// Register a callback for the "ok" response.receive("ok",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {Log.d(TAG,"success "+envelope.toString()); } })// Register a callback for the "error" response.receive("error",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {Log.d(TAG,"error "+envelope.toString()); } });
Payload of new_message containing message details, including:
content: The message content
type: The message content type (media, emoji, text)
Send Message Reply
// Create a new HashMap to store the payload dataMap<String,Object> payload =newHashMap<>();// Add the message contentpayload.put("hi, from nick", content);// Add the message ID as a key with the valuepayload.put(1, message_id);// Send the payload to the channelchannel.push("new_message_reply", payload)// Listen for the "ok" message that signifies successful delivery.receive("ok",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {Log.d(TAG,"success "+envelope.toString()); } })// Listen for the "error" message in case of any issues.receive("error",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope envelope) {Log.d(TAG,"error "+envelope.toString()); } })
Channel Poll Vote
// Define the poll questionString pollQuestion ="What is your favorite color?";// Define a list of available poll optionsList<String> pollOptions =List.of("Red","Blue","Green");// Build the payload with the poll question, content type, and optionsMap<String,Object> payload =newHashMap<>();payload.put("content", pollQuestion);payload.put("content_type","poll");payload.put("options", pollOptions);// Send the new_vote event with the payloadchannel.push("new_vote", payload)// Receive a confirmation message on success.receive("ok",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope response) {System.out.println("Poll Vote Sent: "+response.toString()); } })// Handle any errors that occur.receive("error",newIMessageCallback() { @OverridepublicvoidonMessage(Envelope response) {System.out.println("Error: "+response.toString()); } });
Payload of vote containing vote details, including:
content: The vote question
content_type: The vote content type (media, emoji, text)
options: An array of vote options
For other polls APIs, please refer toSwagger documentation
Attach Media Files to Chat Messages
Attaching media files to chat messages involves obtaining a presigned URL, uploading the file to the presigned URL, and then sending the file information to the chat server.
Obtain a Presigned URL
To obtain a presigned URL, make a POST request to the API endpoint. The request payload should be empty, and the Authorization header should contain your bearer token.
After obtaining the presigned URL, the file can be uploaded to the URL using the PUT method.
File file =event.target.files[0];// Extract the presigned URL from the response objectString signedURL =responseObject.get("presignedURL");Request signedRequest =new Request.Builder()// Set the URL to the presigned URL.url(signedURL)// Set the HTTP method to PUT.PUT(file)// Add an ACL header to allow public read access.addHeader("ACL","public-read")// Add a Content-Disposition header to indicate the file is an attachment.addHeader("Content-Disposition","attachment")// Build the request object.build();// Create a new call using the client and the signed requestclient.newCall(signedRequest).execute();
Use presignedUrl to upload your files
Chat History
Retrieve the chat history using two methods:
By Subscribing to Events
Subscribe to the archived_message event to receive the last 1000 messages in reverse chronological order.
importjava.util.Map;// Subscribe to the "archived_message" eventchannel.on("archived_message", payload -> {// Check if the "messages" field in the payload is a Mapif (payload.get("messages") instanceof Map) {// Cast the "messages" field to a MapMap<?,?> messages = (Map<?,?>) payload.get("messages");// Print a message indicating archived messages were receivedSystem.out.println("Received archived messages: "+ messages); }});
Subscribe to the archived_message_count event to get the total number of messages in the chat history.
importjava.util.Map;// Subscribe to the "archived_message_count" eventchannel.on("archived_message_count", payload -> {// Check if the "page" property is a Mapif (payload.get("page") instanceof Map) {// Cast the "page" property to a MapMap<?,?> page = (Map<?,?>) payload.get("page");// Check if the "count" property is an Integerif (page.get("count") instanceof Integer) {// Cast the "count" property to an Integerint count = (Integer) page.get("count");// Print a message indicating archived messages count is receivedSystem.out.println("Total message count: "+ count); } }});
To retrieve a list of messages in the chat history, trigger the archived_message event to obtain the messages. Specify the size parameter to determine the number of messages you wish to fetch, and set the offset parameter as the starting index of the messages and group_by_date to group messages by date.
importjava.util.HashMap;importjava.util.Map;// Create a map to configure the page information for the archived messageMap<String,Object> payload =newHashMap<>();// Start retrieving messages from the 21st message// Retrieve a maximum of 20 messages// Do not group messages by datepayload.put("page",Map.of("offset",20,"size",20,"group_by_date":false));// Push the payload to the channelchannel.push("archived_message", payload);
To receive the total count of messages at any given time, initiate the archived_message_count trigger and subscribe to the corresponding event by listening for archived_message_count.
channel.push("archived_message_count");
Using the Messages API
Make a GET request to the API endpoint to fetch the chat history for a specific room.
func fetchChatHistory(completion: @escaping (Result<[String: Any], Error>) -> Void) {// Construct the request URL let url =URL(string:"https://api.sariska.io/api/v1/messaging/rooms/{room_name}/messages")!// Modify the request to use the GET methodvar request =URLRequest(url: url)request.httpMethod="GET"request.addValue("Bearer your-token", forHTTPHeaderField:"Authorization")// Send the GET request to the API endpointURLSession.shared.dataTask(with: request) { (data, response, error) in// Check for errorsif let error = error {completion(.failure(error))return }// Check for successful response guard let httpResponse = response as? HTTPURLResponse,httpResponse.statusCode==200else { let statusCode = (response as? HTTPURLResponse)?.statusCode??-1 let errorMessage ="Failed with status code \(statusCode)" let error =NSError(domain:"APIError", code: statusCode, userInfo: [NSLocalizedDescriptionKey: errorMessage])completion(.failure(error))return }// Parse the response datado {if let data = data { let json =try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] completion(.success(json ?? [:])) } else {completion(.success([:])) } } catch {completion(.failure(error)) } }.resume()}
Example usage demonstration
fetchChatHistory { result inswitch result {case.success(let chatHistory):print("Chat history: \(chatHistory)")case.failure(let error):print("Error fetching chat history: \(error)") }}
Fetch Specific Message
Retrieve any specific message from a room. It takes the room ID and message ID as parameters and sends a GET request to the Sariska API to fetch the specified message.
importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.net.HttpURLConnection;importjava.net.URL;importjava.nio.charset.StandardCharsets;importjava.util.HashMap;importjava.util.Map;importjava.util.stream.Collectors;publicclassMessageFetcher {publicstaticvoidfetchSpecificMessage(String messageID,Callback<Map<String,Object>> completion) {try {// Construct the request URLString urlString ="https://api.sariska.io/api/v1/messaging/rooms/{room_name}/messages/"+ messageID;URL url =newURL(urlString);// Construct the requestHttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setRequestProperty("Authorization","Bearer your-token");// Send the GET request to the API endpointint responseCode =connection.getResponseCode();if (responseCode ==HttpURLConnection.HTTP_OK) {BufferedReader reader =newBufferedReader(new InputStreamReader(connection.getInputStream()));String responseData =reader.lines().collect(Collectors.joining());reader.close();// Parse the response dataMap<String,Object> json =newObjectMapper().readValue(responseData,Map.class);completion.onSuccess(json !=null? json :newHashMap<>()); } else {String errorMessage ="Failed with status code "+ responseCode;completion.onFailure(newException(errorMessage)); } } catch (IOException e) {completion.onFailure(e); } }
Utilize the presence.onSync callback to respond to state changes initiated by the server. For instance, to dynamically render the user list upon every list modification, implement the following:
The presence.onJoin and presence.onLeave callbacks allow for handling individual user join and leave events. Here is an instance:
Presence presence =newPresence(channel);// Detect if a user has joined for the 1st time or from another tab/devicepresence.onJoin(userJoinedCallback(id, current, newPres));voiduserJoinedCallback(int id,boolean current,Presence newPres){if(!current){System.out.println("user has entered for the first time "+ newPres); }else{System.out.println("user additional presence "+ newPres); }}// Detect if a user has left from all tabs/devices, or is still a presentpresence.onLeave(userLeftCallback(int id,boolean current,Presence newPres));voiduserLeftCallback(int id,boolean current,Presence newPres){if(!current){System.out.println("user has entered for the first time "+ newPres); }else{System.out.println("user additional presence "+ newPres); }}// Receive presence data from serverpresence.onSync(() -> {presence.list(); //List of users});
Retrieve Presence Information
The presence.list(by:) method retrieves a list of presence information based on the local metadata state. By default, it returns the entire presence metadata. Alternatively, a listBy function can be provided to filter the metadata for each presence.
For instance, if a user is online from multiple devices, each with a metadata status of "online," but their status is set to "away" on another device, the application might prefer to display the "away" status in the UI.
The following example defines a listBy function that prioritizes the first metadata registered for each user, representing the first tab or device they used to connect:
List onlineUsers =presence.list();
User Typing
Send information about a user who is typing. This information can be used to update the chat interface, such as displaying a "user is typing" indicator next to the user's name.
Send User Typing Event
When a user starts typing, the following code sends a typing event to other peers.