Skip to main content
Android
iOS
Web
Windows
Unity
Flutter
React Native

Manage server-side messages

The Chat SDK stores historical messages on the chat server. When a chat user logs in from a different device, you can retrieve the historical messages from the server, so that the user can also browse these messages on the new device. Additionally, the Chat SDK supports adding tags to conversation, with a maximum of 20 tags allowed per conversation.

This page introduces how to use the Chat SDK to retrieve, delete, and tag messages messages and conversations.

Understand the tech

The Chat SDK uses ChatManager to retrieve historical messages from the server. The following are the core methods:

  • asyncFetchConversationsFromServer: Retrieves a list of conversations stored on the server.
  • asyncFetchHistoryMessages: Retrieves historical messages of a conversation from the server according to FetchMessageOption, the parameter configuration class for retrieving historical messages.
  • asyncPinConversation: Pins conversations.
  • asyncFetchPinnedConversationsFromServer: Retrieves pinned conversations.
  • asyncPinMessage: Pins a message in a conversation.
  • asyncUnPinMessage: Unpin a message in a conversation.
  • asyncGetPinnedMessagesFromServer: Get a list of pinned messages in a conversation.
  • removeMessagesFromServer: One-way deletion of historical messages on the server based on message time or message ID.
  • deleteConversationFromServer: Deletes conversations and their historical messages from the server.
  • asyncAddConversationMark: Tags a conversation.
  • asyncRemoveConversationMark: Removes a conversation tag.
  • asyncGetConversationsFromServerWithCursor: Queries conversations from the server by a conversation tag.

Prerequisites

Before proceeding, ensure that you meet the following requirements:

  • You have integrated the Chat SDK, initialized the SDK and implemented the functionality of registering accounts and login. For details, see Chat SDK quickstart.
  • You understand the API call frequency limits as described in Limitations.

Implementation

This section shows how to implement retrieving conversations and messages.

Retrieve a list of conversations from the server

Call asyncFetchConversationsFromServer to retrieve conversations from the server with pagination. The SDK returns the conversation list in the reverse chronological order of when conversations are active (the timestamp of the last message in the conversation). In the conversation list, each conversation object contains the conversation ID, conversation type, whether the conversation is pinned, the pinned time (the value is 0 for an unpinned conversation), and the last message in the conversation. After the conversation list is retrieved from the server, the local conversation list will be updated accordingly. We recommend calling this method when the app is first installed, or when there is no conversation on the local device. Otherwise, you can call getAllConversations to retrieve conversations on the local device.

For each end user, the server stores 100 conversations by default. When this limit is exceeded, new conversations will start overwriting the old ones. If the entire message history in a conversation expires, the conversation becomes empty. When pulling the conversation list from the server, these empty conversations are not included by default. To include them, set EMOptions#isLoadEmptyConversations to true when initializing the SDK. In this case, empty conversations will occupy the conversations pull quota, regardless of whether they are needed when pulling. To change this, contact support@agoro.io.


_29
String cursor = "";
_29
limit: The number of conversations that you expect to get on each page. The value range is [1,50].
_29
int limit = 40;
_29
List<Conversation> conversations = new ArrayList<>();
_29
doAsyncFetchConversationsFromServer(limit,cursor,conversations);
_29
_29
private void doAsyncFetchConversationsFromServer(final int limit, final String cursor,List<Conversation> conversations){
_29
ChatClient.getInstance().chatManager().asyncFetchConversationsFromServer(limit, cursor, new ValueCallBack<CursorResult<Conversation>>() {
_29
@Override
_29
public void onSuccess(CursorResult<Conversation> value) {
_29
if (value != null ) {
_29
List<Conversation> list = value.getData();
_29
if (list != null && list.size() > 0) {
_29
conversations.addAll(list);
_29
}
_29
String newCursor = value.getCursor();
_29
if( !TextUtils.isEmpty(newCursor)) {
_29
doAsyncFetchConversationsFromServer(limit, newCursor, conversations);
_29
}
_29
}
_29
_29
}
_29
_29
@Override
_29
public void onError(int error, String errorMsg) {
_29
_29
}
_29
});
_29
}

Retrieve message history of the specified conversation

After retrieving conversations, you can retrieve historical messages from the server.

You can set the search direction to retrieve messages in the chronological or reverse chronological order of when the server receives them, the message type, the time period, the message sender, as well as whether to save the retrieved message to the local database.

If you have integrated Chat SDK after June 8, 2023, you can retrieve historical messages even before joining the Chat Group. For earlier implementations, contact support@agora.io to enable this.

The Agora Chat server stores the full message history for a certain period of time depending on your subscribed Chat plan. After an end user logs back into Agora Chat, the servers automatically send offline messages to them, that is, messages transmitted when that end user was offline. Offline messages are a subset of the full message history stored on Agora Chat server. Sending only a subset of messages prevents distributing too many messages to a single device, which can overwhelm it and slow down the end user login. Agora Chat server stores and manages these offline messages for every end user in the following way:

  • 1:1 private chat: Store 500 offline messages by default;
  • Chat Group: Store 200 offline messages by default;
  • Chatroom: Doesn't store offline messages. However, by default, whenever an end user joins a chatroom, Agora Chat servers push the 10 latest messages/chatroom to them, and this number can be adjusted to 200 messages/chatroom without additional charges.

After the GA release of Agora Chat 1.3, you can log in to Agora Console and enable the chatroom message history retrieval feature. After that, Agora Chat servers will stop automatically pushing chatroom messages to new members and you can follow the guidance below to retrieve chatroom message history.

For users to receive more offline messages, use the client API or a webhook to sync with Agora Chat's server. End users can also store additional messages on their local database.

To ensure data reliability, we recommend retrieving less than 50 historical messages for each method call. To retrieve more than 50 historical messages, call this method multiple times. Once the messages are retrieved, the SDK automatically updates these messages in the local database.

We recommend that you retrieve 20 messages each time, with a maximum of 50. During paginated query, if the total number of messages that meet the query conditions is greater than the number of pageSize, the number of messages of pageSize will be returned. If it is less than the number of pageSize, the actual number will be returned. When the message query is completed, the number of returned messages is less than the number of pageSize.

Refer to the following code sample:


_36
String conversationId=" ";
_36
Conversation.ConversationType type=Conversation.ConversationType.Chat;
_36
FetchMessageOption option=new FetchMessageOption();
_36
//for example
_36
//option.setIsSave(true);
_36
int pageSize = 40;
_36
String cursor = "";
_36
List<ChatMessage> messages = new ArrayList<>();
_36
doAsyncFetchHistoryMessages(conversationId,type,pageSize,cursor,option,messages);
_36
_36
private void doAsyncFetchHistoryMessages(String conversationId,
_36
Conversation.ConversationType type,
_36
int pageSize,String cursor,
_36
FetchMessageOption option,
_36
List<ChatMessage> messages){
_36
ChatClient.getInstance().chatManager().asyncFetchHistoryMessages(conversationId, type, pageSize, cursor, option, new ValueCallBack<CursorResult<ChatMessage>>() {
_36
@Override
_36
public void onSuccess(CursorResult<ChatMessage> value) {
_36
if (value != null ) {
_36
List<ChatMessage> list = value.getData();
_36
if (list != null && list.size() > 0) {
_36
messages.addAll(list);
_36
}
_36
String newCursor = value.getCursor();
_36
if( !TextUtils.isEmpty(newCursor)) {
_36
doAsyncFetchHistoryMessages(conversationId, type, pageSize, newCursor, option, messages);
_36
}
_36
}
_36
}
_36
_36
@Override
_36
public void onError(int error, String errorMsg) {
_36
_36
}
_36
});
_36
}

Pin a conversation

To keep track of an important conversation, you can pin it to the top of your conversation list. You can pin up to 50 conversations. The pinned state is stored on the server. In a multi-device login use-case, if you pin or unpin a conversation, other login devices will receive the CONVERSATION_PINNED or CONVERSATION_UNPINNED events.

Refer to the following code example to pin a conversation:


_9
ChatClient.getInstance().chatManager().asyncPinConversation(conversationId, true, new CallBack() {
_9
@Override
_9
public void onSuccess() {
_9
}
_9
_9
@Override
_9
public void onError(int code, String error) {
_9
}
_9
});

Pin a message

You can call ChatManager#asyncPinMessage to pin a message to the top of a one-to-one chat, chat group, or chat room. When the pinned status of a message changes, other members in the group or chat room conversation will receive the MessageListener#onMessagePinChanged event. In the case of multi-device login, the updated top status will be synchronized to other logged-in devices, and other devices will receive the MessageListener#onMessagePinChanged event, respectively.

In group and chat room conversations, multiple users can pin the same message to the top. The latest pinned message will overwrite the earlier information. That is, the MessagePinInfo user ID and pin time will correspond to the latest pinned message.

If the message is stored locally but deleted on the server due to expiration, the message will fail to be pinned to the top.

For a single conversation, 20 messages can be pinned to the top by default.


_16
ChatClient.getInstance().chatManager().asyncPinMessage(message.getMsgId(), new CallBack() {
_16
@Override
_16
public void onSuccess() {
_16
_16
}
_16
_16
@Override
_16
public void onError(int code, String error) {
_16
_16
}
_16
_16
@Override
_16
public void onProgress(int progress, String status) {
_16
_16
}
_16
});

Unpin a message

You can call ChatManager#asyncUnPinMessage to unpin a message in a group or room chat. As with pinned messages, other members of the group or chat room will receive the MessageListener#onMessagePinChanged event when the pinned message is unpinned. In the case of multi-device login, the updated pin status will be synchronized to other logged-in devices, and other devices will receive the MessageListener#onMessagePinChanged event, respectively.

Any user in a group or room can unpin a message, regardless of who pinned it. After unpinning a message, Message#pinnedInfo is returned empty and the message is no longer included in the pinned message list.


_16
ChatClient.getInstance().chatManager().asyncUnPinMessage(message.getMsgId(), new CallBack() {
_16
@Override
_16
public void onSuccess() {
_16
_16
}
_16
_16
@Override
_16
public void onError(int code, String error) {
_16
_16
}
_16
_16
@Override
_16
public void onProgress(int progress, String status) {
_16
_16
}
_16
});

Get pinned messages in a single conversation

You can call ChatManager#asyncGetPinnedMessagesFromServer to get the pinned messages from a single conversation from the server. The SDK returns messages in the reverse order of pinning.

Note
  • If a message expires on the server or the user deletes it unilaterally from the server after pinning, such user will not be able to pull it when pulling roaming messages. However, this user and other users will be able to pull this message from the pinned message list.
  • If the user withdraws a message after pinning, the message will be removed from the server; other users will not be able to pull it when they pull the pinned message list from the server.

_11
ChatClient.getInstance().chatManager().asyncGetPinnedMessagesFromServer(conversationId, new ValueCallBack<List<ChatMessage>>() {
_11
@Override
_11
public void onSuccess(List<ChatMessage> pinedMessages) {
_11
_11
}
_11
_11
@Override
_11
public void onError(int error, String errorMsg) {
_11
_11
}
_11
});

Get pinned details of a single message

You can call MessagePinInfo to get the pinned details of a single message.

  • If the message is pinned, this class returns the time of pinning and the user ID of the user who has pinned it.
  • If the message is not pinned, this class is returned empty.

_7
MessagePinInfo emPinnedInfo = message.pinnedInfo();
_7
if(emPinnedInfo!=null) {
_7
long pinTime = emPinnedInfo.pinTime();
_7
String operatorId = emPinnedInfo.operatorId();
_7
}else{
_7
//If the value is empty, the message is in the unpinned state.
_7
}

Delete historical messages from the server unidirectionally

Call removeMessagesFromServer to delete historical messages one way from the server. You can remove a maximum of 50 messages from the server each time. Once the messages are deleted, you can no longer retrieve them from the server. The deleted messages are automatically removed from your local device. Other chat users can still get the messages from the server.


_18
// Delete messages by time stamp
_18
Conversation conversation = ChatClient.getInstance().chatManager().getConversation(username);
_18
conversation.removeMessagesFromServer(time, new CallBack() {
_18
@Override
_18
public void onSuccess() {}
_18
_18
@Override
_18
public void onError(int code, String desc) {}
_18
});
_18
_18
// Delete messages by message ID
_18
conversation.removeMessagesFromServer(msgIdList, new CallBack() {
_18
@Override
_18
public void onSuccess() { }
_18
_18
@Override
_18
public void onError(int code, String desc) { }
_18
});

Call deleteConversationFromServer to delete conversations and their historical messages unidirectionally from the server. After the conversations and messages are deleted from the server, you can no longer get them from the server. The deleted conversations still exist on your local device, but the messages are automatically removed from the device. Other chat users can still get the conversations and their historical messages from the server.


_10
ChatClient.getInstance().chatManager().deleteConversationFromServer(conversationId, conversationType, isDeleteServerMessage, new CallBack() {
_10
@Override
_10
public void onSuccess() {
_10
_10
}
_10
_10
@Override
_10
public void onError(int code, String error) {
_10
}
_10
});

Tag a conversation

To tag a conversation, use the asyncAddConversationMark method. This method allows you to add tags to both local and server-side conversations. Each conversation can have up to 20 tags. This feature applies only to private chats and chat groups.

After adding a tag, you can retrieve the tagged conversations from the server by calling the getConversationsFromServerWithCursor:pageSize:completion method. The returned conversation objects will include the conversation tag, which you can obtain using the marks property.

If the server conversation list reaches its limit (default 100 conversations per user), inactive conversations will be deleted based on conversation activity (timestamp of the latest message). Consequently, the conversation tags of these deleted conversations will also be removed.

Hint
Adding tags to a conversation, such as stars, does not affect other logic of the conversation, such as the number of unread messages in it.

_14
String conversationId = "Derry";
_14
List<String> ids=new ArrayList<>();
_14
ids.add(conversationId);
_14
ChatClient.getInstance().chatManager().asyncAddConversationMark(ids, Conversation.EMMarkType.MARK_0, new CallBack() {
_14
@Override
_14
public void onSuccess() {
_14
_14
}
_14
_14
@Override
_14
public void onError(int code, String error) {
_14
_14
}
_14
});

To use customized tags, use the following code:


_7
Map<EMConversation.EMMarkType,String> mapping=new HashMap<>();
_7
mapping.put(EMConversation.EMMarkType.MARK_0,"important");
_7
mapping.put(EMConversation.EMMarkType.MARK_1,"normal");
_7
mapping.put(EMConversation.EMMarkType.MARK_2,"unimportant");
_7
mapping.put(EMConversation.EMMarkType.MARK_3,"boys");
_7
mapping.put(EMConversation.EMMarkType.MARK_4,"girls");
_7
……

Remove conversation tag

You can call asyncRemoveConversationMark to remove tag of a conversation. Tags can be removed for up to 20 conversations at a time. Calling this method removes both the local and server-side tags.


_14
String conversationId = "Derry";
_14
List<String> ids=new ArrayList<>();
_14
ids.add(conversationId);
_14
ChatClient.getInstance().chatManager().asyncRemoveConversationMark(ids, Conversation.EMMarkType.MARK_0,new CallBack() {
_14
@Override
_14
public void onSuccess() {
_14
_14
}
_14
_14
@Override
_14
public void onError(int code, String error) {
_14
_14
}
_14
});

Query conversations from the server by the conversation tag

You can use the asyncGetConversationsFromServerWithCursor method to retrieve a list of conversations from the server based on the tag. The SDK returns the conversation list in reverse order of the conversation tag's timestamp. Each conversation object includes the following information:

  • Conversation ID
  • Conversation type
  • Pinned status (whether it is pinned or not)
  • Pin time (For an unpinned conversation, the value is 0)
  • Conversation tag
  • Latest message

After fetching the conversation list from the server, the local conversation list is updated accordingly:


_29
//All the query results are put into `result`.
_29
List<Conversation> result = new ArrayList<>();
_29
//cursor: The starting position of the query. Pass in an empty string for the first call of the method and the SDK starts to get from the conversation with the latest marked operation.
_29
String cursor = "";
_29
// filter: Conversation query options, including conversation marks and the number of conversations obtained per page (up to 10).
_29
// For example, query all conversations marked with Conversation.EMMarkType.MARK_0 on the server.
_29
ConversationFilter filter = new ConversationFilter(Conversation.EMMarkType.MARK_0, 10);
_29
doAsyncGetConversationsFromServerWithCursor(result, cursor, filter);
_29
_29
private void doAsyncGetConversationsFromServerWithCursor(List<Conversation> result, @NonNull String cursor, @NonNull ConversationFilter filter) {
_29
ChatClient.getInstance().chatManager().asyncGetConversationsFromServerWithCursor(cursor, filter, new ValueCallBack<CursorResult<Conversation>>() {
_29
@Override
_29
public void onSuccess(CursorResult<Conversation> value) {
_29
List<Conversation> datas=value.getData();
_29
if(!CollectionUtils.isEmpty(datas)){
_29
result.addAll(datas);
_29
}
_29
String cursor_ = value.getCursor();
_29
if(!TextUtils.isEmpty(cursor_)){
_29
doAsyncGetConversationsFromServerWithCursor(result,cursor_,filter);
_29
}
_29
}
_29
_29
@Override
_29
public void onError(int error, String errorMsg) {
_29
_29
}
_29
});
_29
}

Query local conversations by the conversation tag

For local conversations, you can call the getAllConversations method to obtain all local conversations and then perform conversation filtering yourself. The following is an example of querying all local conversations marked with EMConversation.EMMarkType.MARK_0:


_15
//All the query results are put into result.
_15
List<Conversation> result=new ArrayList<>();
_15
Map<String, Conversation> localConversations = ChatClient.getInstance().chatManager().getAllConversations();
_15
if(localConversations!=null&&!localConversations.isEmpty()){
_15
for(Conversation conversation:localConversations.values()){
_15
Set<Conversation.EMMarkType> marks = conversation.marks();
_15
if(marks!=null&&!marks.isEmpty()){
_15
for(Conversation.EMMarkType mark:marks){
_15
if(mark==Conversation.EMMarkType.MARK_0){
_15
result.add(conversation);
_15
}
_15
}
_15
}
_15
}
_15
}

Get all tags of a local conversation

You can call Conversation#marks to get all the tags of a local conversation. The sample code is as follows:


_1
Set<Conversation.EMMarkType> conversationMarks = ChatClient.getInstance().chatManager().getConversation("conversationId").marks();

Next steps

After implementing retrieving messages, you can refer to the following documents to add more messaging functionalities to your app:

vundefined