Messages
im.messages handles the message list, sending, recall, delete for me, reactions, pinning, read receipts and local search. For message fields, limits and content formats, see Message format in the server docs.
| You want to | See |
|---|---|
| Show a conversation's messages and page through history | Open the message list |
| Send text, images, files or custom messages | Send content |
| Mention members, quote, request read receipts, handle send failures | Send options and the queue |
| Recall, delete, react, pin | Recall, delete, reactions and pinning |
| Search locally, group read counts | Lookup and receipts |
| Send temporary signals that aren't stored | Online-only messages |
Open the message list
val list = im.messages.open(conversationKey)
try {
list.setViewing(atBottom = true, visible = true)
list.snapshot.collect { state ->
println(state.items.size)
println(state.error?.code)
}
} finally {
list.setViewing(visible = false)
list.close()
}The snapshot has items, gaps, hasOlder, hasNewer, loading, pinned and error:
loadOlder()andloadNewer()load older or newer messages;jumpTo(seq)jumps to around a message.gapsare ranges not loaded yet. Those withkindskippedcan be loaded withloadGap(gap);rejoingaps are messages from before the user rejoined the group, which they can't see and can't be loaded.
Call setViewing when the screen scrolls or is shown or hidden, to report whether the user is at the newest message and whether the list is visible. With autoMarkRead on, the SDK moves the read position based on that; otherwise call list.markRead() when the user sees new messages. For a full lifecycle example, see UI and lifecycle.
Send content
import com.deeprespond.im.*
val message = im.messages.send(
SendTarget.User("bob"),
OutgoingContent.Text("你好"),
SendOptions(waitUntilSent = true),
)SendTarget has three forms: User(username) for one-to-one, Group(groupId) for groups, and Conversation(conversationKey) for an existing conversation.
| Content | How to build it |
|---|---|
| Text | OutgoingContent.Text(text) |
| Image | OutgoingContent.Image(file, original = false) |
| Voice | OutgoingContent.Voice(file, duration) |
| Video | OutgoingContent.Video(file) |
| File | OutgoingContent.File(file, name?) |
| Custom | OutgoingContent.Custom(body), taking a JsonObject or a Map |
| Location | OutgoingContent.Location(latitude, longitude, name?, address?) |
| Already uploaded attachment | OutgoingContent.Uploaded(type, body) |
| Forward | OutgoingContent.Forward(message) |
Attachments are passed as DRFile; for creating one from a URI, recording and preprocessing before upload, see Files.
im.messages.send(
SendTarget.Group(groupId),
OutgoingContent.Custom(mapOf("card" to "order", "order_id" to "order-1")),
SendOptions(mentionUsernames = listOf("bob"), needReceipt = true),
)Send options and the queue
| SendOptions field | Default | Purpose |
|---|---|---|
ext | null | JsonObject extension fields |
mentionUsernames, mentionAll | null, false | Mention members, mention everyone |
replyToSeq | null | Quote a sequence number in the same conversation |
needReceipt | false | Request a group read receipt |
excludeFromUnread | false | Don't count toward unread; whether it's allowed depends on the policies |
pushDisabled | null | Whether to turn off push for this message |
waitUntilSent | false | Return only after the server confirms |
By default send returns once the message is written to the send queue. The message's local status goes through queued (waiting), uploading and sending, ending in sent or failed. Unsent messages may have null messageId, seq and conversationId, so identify them by clientMsgId; upload progress and errors are in message.local.
- Once a message is in the queue, cancelling the caller's coroutine only stops waiting for the result; the message is still sent.
discard(clientMsgId)deletes a waiting or failed local message and cancels its upload;resend(clientMsgId)resends a failed message.- When the network returns the SDK continues the queue automatically, so don't call
sendrepeatedly for the same message.
Recall, delete, reactions and pinning
if (im.messages.canRecall(message)) {
val id = message.conversationId
val seq = message.seq
if (id != null && seq != null) im.messages.recall(id, seq)
}
im.messages.react(conversationId, seq, "like")
im.messages.unreact(conversationId, seq, "like")
im.messages.setPinned(conversationId, seq, true)Whether recalling and pinning succeed depends on the server's permissions and time limits. deleteForMe(conversationId, seqs) deletes messages for the user only, up to 100 per call; reactionUsers lists who reacted with a given reaction. The client currently has no method for editing messages; don't copy the edit endpoint from the server REST API.
Lookup and receipts
lookup(conversationId, seqs)fetches messages by sequence number, returningitemsand themissingones that couldn't be fetched.searchLocal(keyword, conversationId?, limit = 50)only searches text messages saved on this device; it isn't a full server-side search.receipts(conversationId, seqs)gets read counts for group messages;getReceiptreads the most recent result from memory.receiptUsers(..., ReceiptStatus.READ / UNREAD)lists members who have or haven't read, page by page; whether they can be viewed depends on the app's policies.
Online-only messages
sendOnline(target, body, ext?) can only send custom content: it skips the send queue, isn't stored, isn't received by offline users, and isn't retried on failure. While the connection is paused it fails with network_error (paused). Recipients get it through the MessageOnline event. Use it for temporary signals such as "the other user is doing something", not for notifications that must arrive.
For all methods and parameters, see MessagesModule.
