Messaging
This page highlights the steps you need to follow to begin integrating chat messaging into your products
The messge payload is always the same regardless of which Development Kit the user is using. Users also have a choice on what type of message they want to send.‌

Message Types

Amity supports the sending and receiving of 5 types of messages. They are Text, Image, Audio, File, and Custom.

Image

Amity will automatically optimize the image and when queried, will return the image in small, medium and large sizes. If the image is marked as isFull on upload, the original size of the image can also be returned.

Specifications on image size

When an image is uploaded, it is automatically resized into multiple sizing options. The size of the image is determined by its longest dimension (in pixels) with the aspect ratios being unchanged.
The maximum file size of an image cannot exceed 1 GB.‌

Message Format

When a user is observing messages, the message will always appear in this format.
1
{
2
"status": "string",
3
"data": {
4
"messages": [
5
{
6
"messageId": "string",
7
"parentId": "string",
8
"childrenNumber": 0,
9
"channelId": "string",
10
"userId": "string",
11
"type": "string",
12
"tags": [
13
"string"
14
],
15
"data": {
16
"text": "string"
17
},
18
"isDeleted": true,
19
"channelSegment": 0,
20
"createdAt": "2020-01-02T06:54:59.244Z",
21
"updatedAt": "2020-01-02T06:54:59.244Z",
22
"editedAt": "2020-01-02T06:54:59.244Z",
23
"flagCount": 0,
24
"hashFlag": {
25
"bits": 0,
26
"hashes": 0,
27
"hash": [
28
0
29
]
30
},
31
"reactions": {
32
"reactionName": 1,
33
},
34
"reactionsCount": 0,
35
"myReactions": [
36
"string"
37
]
38
}
39
],
40
"users": [
41
{
42
"userId": "string",
43
"roles": [
44
"string"
45
],
46
"displayName": "string",
47
"flagCount": 0,
48
"metadata": {},
49
"hashFlag": {
50
"bits": 0,
51
"hashes": 0,
52
"hash": [
53
0
54
]
55
},
56
"createdAt": "2020-01-02T06:54:59.244Z",
57
"updatedAt": "2020-01-02T06:54:59.244Z"
58
}
59
]
60
}
61
}
Copied!

Messages Description

Name
Data Type
Description
Attributes
messageId
string
The id of this message
​Content
parentId
string
The messageId of the parent of this message
​Content
childrenNumber
integer
The number of messages with parentId of this message
​Content
channelId
string
The name of the channel this message was created in
​Content
userId
string
The name of the user this message was created by
​Content
type
string
The message type
enum*: text custom image file
tags
Array.<string>
The message tags
​Content
data
Object
The message data (any text will be stored in text key)
text: Text message
isDeleted
boolean
The message has been marked as deleted
​Content
channelSegment
integer
The sequence number of a message in channel
​Content
createdAt
date
The date/time the message was created at
​Content
updatedAt
date
The date/time the message was updated at
​Content
editedAt
date
The date/time the message was edited at
​Content
flagCount
integer
The number of users that have flagged this message
​Content
hashFlah
Object
A hash for checking internally if this message was flagged by the user
​Content
reactions
Object
The reaction data (stored as a reactionName and counter key/value pair)
Example: { like: 1, dislike: 2 }
reactionsCount
integer
The total number of reactions on this message
​Content
myReactions
Array.<string>
A list of user's reactions on this message
​Content
Messages are JSON content containers that can weigh up to 100KB and will be synchronized among all channel users in real-time. If a message requires larger binary data (such as when sending files), we recommend uploading the data to another cloud storage service, such as AWS S3, and store the URL to the content in the message data.
In addition to the JSON message type, the SDK also provides support for common text and image message types. These additional types are built on top of the standard JSON message layer.
In case of image messages, the SDK freely provides a cloud storage service that will process and store all images uploaded: you don't need to set up and manage a separate cloud storage service for this common case.
All messaging methods are contained in a AmityMessageRepository class. Before calling any messaging methods, you must ensure to first instantiate a repository instance using the AmityChatClient instance you created on setup.
1
val messageRepository = AmityChatClient.newMessageRepository();
Copied!

Sending Messages

All message sending methods are designed to be robust enough to work under any network conditions. When you send any message, that message will automatically be put into a queue in case of any unforeseen unstable network conditions. Once the SDK reconnects to the server, it will automatically resend all the queued messages.
Additionally, sent messages are always returned in message queries, even before they have been delivered to the server. This provides the user with a fluid messaging flow: when a user sends a message, that sent message will appear in the message stream right away (instead of waiting until it has been confirmed by the server). To check or display the current status of message delivery for your application, use the syncState property in the message model; for web you should useAmityMessage.getState() method in the AmityMessage object.
1
messageRepository.createMessage(channelId)
Copied!

Text Messages

Initiate the messaging with the following scripts, depending on your platform of choice
1
messageRepository.createMessage(channelId)
2
.with()
3
.text("Hello Social Cloud!")
4
.build()
5
.send()
6
.subscribe()
Copied!
The limit for sending text messages is 20,000 characters per text message. Messages exceeding that limit will return an error and will not be sent.

Image Message

To send an image message, you must pass in a valid image URL. The SDK will automatically send it to the server. You can also pass in an optional caption as part of the message. You can add up to 1,000 characters of text caption per message.
To send an image in original size, set optional isFullImage() to true. Note: File size is limited to 1 GB
1
messageRepository.createMessage(channelId)
2
.with()
3
.image(imageUri)
4
.caption("It's a beautiful day")
5
.isFullImage(true)
6
.build()
7
.send()
8
.subscribe()
Copied!

Image message status sending

1
messageRepository.getMessages(getChannelId())
2
.build()
3
.query()
4
.observe(this, Observer { messages ->
5
6
// messages -> LiveData<PagedList<AmityMessage>>
7
// check message status sending of each message
8
val message = messages.first()
9
message.isDeleted() // message is deleted
10
when(m.getState()) {
11
AmityMessage.State.CREATED -> {
12
// save message into local
13
}
14
AmityMessage.State.UPLOADING -> {
15
// send request to server
16
}
17
AmityMessage.State.SYNCING -> {
18
// upload image to server
19
}
20
AmityMessage.State.SYNCED -> {
21
// image uploaded
22
}
23
AmityMessage.State.FAILED -> {
24
// send request to server failed
25
// upload image to server failed
26
}
27
}
28
})
Copied!

Image status uploading

1
fileRepository.getUploadInfo(data.getMessageId())
2
.subscribeOn(Schedulers.io())
3
.observeOn(AndroidSchedulers.mainThread())
4
.doOnNext { uploadInfo ->
5
// percentage uploaded
6
uploadInfo.getProgressPercentage()
7
}
8
.subscribe()
Copied!

File Message

To send a file message, you must pass in a valid file URL. The SDK will automatically send it to the server. You can also pass in an optional caption as part of the message. You can add up to 1,000 characters of text caption per message.
Note: File size is limited to 1 GB
1
messageRepository.createMessage(channelId)
2
.with()
3
.file(fileUri)
4
.caption("It's a beautiful day")
5
.build()
6
.send()
7
.subscribe()
Copied!

File message status sending

1
messageRepository.getMessages(getChannelId())
2
.build()
3
.query()
4
.observe(this, Observer { messages ->
5
6
// messages -> LiveData<PagedList<AmityMessage>>
7
// check message status sending of each message
8
val message = messages.first()
9
message.isDeleted() // message is deleted
10
when(m.getState()) {
11
AmityMessage.State.CREATED -> {
12
// save message into local
13
}
14
AmityMessage.State.UPLOADING -> {
15
// send request to server
16
}
17
AmityMessage.State.SYNCING -> {
18
// upload file to server
19
}
20
AmityMessage.State.SYNCED -> {
21
// file uploaded
22
}
23
AmityMessage.State.FAILED -> {
24
// send request to server failed
25
// upload file to server failed
26
}
27
}
28
})
Copied!

Image status uploading

1
fileRepository.getUploadInfo(data.getMessageId())
2
.subscribeOn(Schedulers.io())
3
.observeOn(AndroidSchedulers.mainThread())
4
.doOnNext { uploadInfo ->
5
// percentage uploaded
6
uploadInfo.getProgressPercentage()
7
}
8
.subscribe()
Copied!

Custom Message

With a custom message, you can send a JsonObect of your choice as part of the message.
1
val customObject = JsonObject()
2
customObject.addProperty("url", "https://docs.amity.technology");
3
4
messageRepository.createMessage(channelId)
5
.with()
6
.custom(customObject)
7
.build()
8
.send()
9
.subscribe()
Copied!

Message Reply

To reply to a message, specify the messageId with parentId() method:
1
messageRepository.createMessage(channelId)
2
.parentId(getMessageId())
3
.with()
4
.text("Hello Social Cloud!")
5
.build()
6
.send()
7
.subscribe()
Copied!

Message Query

To query for a list of all messages in a channel:
1
messageRepository.getMessageCollection(channelId)
2
.build()
3
.query()
4
.subscribe( { adapter.submitList(it) } )
Copied!
This method will return all messages in the specified channel as Flowable<PagedList<AmityMessage>>

Stack From End

While the SDK will always return messages in chronological order, developers can ask for the messages to be returned starting from the oldest first, or the newest first.
On a typical messaging application, the messages are fetched from the latest (newest) first, and then more older messages are explored. (When stack from end is set to true, the list fills its content starting from the bottom of the view)
1
messageRepository.getMessages(channelId)
2
.stackFromEnd(true)
3
.build()
4
.query()
5
.subscribe( { adapter.submitList(it) } )
Copied!
There are other situations where fetching the oldest messages is preferred, for example for archiving purposes or in community forums. (When stack from end is set to false, the list fills its content starting from the top of the view)
1
messageRepository.getMessages(channelId)
2
.stackFromEnd(false)
3
.build()
4
.query()
5
.subscribe( { adapter.submitList(it) } )
Copied!
Note: Please make sure that stackFromEnd using obtain MessageCollection is the same as stackFromEnd of RecyclerView's LayoutManager. Otherwise it may cause a jumping issue.

Message Filtering

Like channels, we also have various ways to obtain messages that only match specific criteria:
    the includingTags and excludingTags parameters let you filter messages based on the tags set (or not set) in each message
    the parentId parameter lets you filter messages by their relationship:
      when no parentId is passed, any message will match.
      when null parentId is passed, query for all messages without a parent.
      when non-null parentId is passed: query for all messages with the parentId as parent.
1
val includingTags = AmityTags()
2
includingTags.add("games")
3
4
val excludingTags = AmityTags()
5
excludingTags.add("staff-only")
6
7
// queries for messages
8
messageRepository.getMessages(channelId)
9
.stackFromEnd(stackFromEnd)
10
.build()
11
.query()
12
13
// queries for messages tagged as "games"
14
messageRepository.getMessages(channelId)
15
.stackFromEnd(stackFromEnd)
16
.includingTags(includingTags)
17
.build()
18
.query()
19
20
// queries for messages tagged as "games" and not tagged as "staff-only"
21
messageRepository.getMessages(channelId)
22
.stackFromEnd(stackFromEnd)
23
.includingTags(includingTags)
24
.excludingTags(excludingTags)
25
.build()
26
.query()
27
28
val parentId = "999";
29
30
// queries for children messages of a message with id "999"
31
messageRepository.getMessages(channelId)
32
.parentId(parentId)
33
.stackFromEnd(stackFromEnd)
34
.build()
35
.query()
36
37
// queries for children messages of a message with id "999" and tagged as "games"
38
messageRepository.getMessages(channelId)
39
.parentId(parentId)
40
.stackFromEnd(stackFromEnd)
41
.includingTags(includingTags)
42
.build()
43
.query()
44
45
// queries for children messages of a message with id "999", tagged as "games" and not tagged as "staff-only"
46
messageRepository.getMessages(channelId)
47
.parentId(parentId)
48
.stackFromEnd(stackFromEnd)
49
.includingTags(includingTags)
50
.excludingTags(excludingTags)
51
.build()
52
.query()
53
Copied!
In the case where you only want to fetch an individual message from a channel, you can use the getMessage() method:
1
messageRepository.getMessage(messageId)
2
.doOnNext { message -> {
3
4
}
5
.subscribe()
Copied!

Viewing Text Message

1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.TEXT -> {
4
val text = data.getText()
5
}
6
}
Copied!

Viewing Image Message

To get an image URL from AmityMessage:
1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.IMAGE -> {
4
val imageUrl = data.getImage().getUrl()
5
}
6
}
Copied!
Calling getUrl() without specifying image size returns a URL of a medium-size image.
Please use the appropriate image size depending on your use-case to save bandwidth.
    SMALL is used for image thumbnails, with a maximum image size of 160 pixels per dimension. For example, this should be used for small previews in an image gallery that displays a large number of images in a grid.
    MEDIUM is used for standard image display, with a maximum image size of 600 pixels per dimension.
    LARGE is used for full-screen image display, with a maximum image size of 1500 pixels per dimension.
    FULL is used to get the original image. This size is only valid if the image is uploaded with the method isFullImage() set to true. If a FULL sized image is not available, a LARGE sized image will be returned instead.
To render image with Glide
1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.IMAGE -> {
4
val imageUrl = data.getUrl()
5
Glide.with(context)
6
.load(imageUrl)
7
.into(holder.imageView)
8
}
9
}
Copied!
To render image with Picasso
1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.IMAGE -> {
4
val imageUrl = data.getUrl()
5
val picasso = Picasso.Builder(context).build()
6
picasso.load(imageUrl)
7
.into(imageView);
8
}
9
}
Copied!

Viewing File Message

To get file URL and file name from AmityMessage
1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.FILE -> {
4
val fileName = data.getFile().getFileName()
5
val fileUrl = data.getFile().getUrl()
6
}
7
}
Copied!

Viewing Custom Message

To get customedJsonObject from AmityMessage
1
val data = message.getData()
2
when (data) {
3
is AmityMessage.Data.CUSTOM -> {
4
val json = data.getRawData()
5
}
6
}
Copied!

Edit and Delete Messages

You can only perform edit and delete operations on messages you've sent. Once the operation is complete, the message's editedAt will be set to the current time. This allows you to provide UI to the user to inform the user of specific messages that have been edited, if needed.
Currently, the Chat SDK has only 2 editable data types, TEXT and CUSTOM
1
val data : AmityMessage.Data.TEXT = ...
2
data.edit()
3
.text(text)
4
.build()
5
.apply()
6
.subscribe()
7
8
9
val data : AmityMessage.Data.CUSTOM = ...
10
data.edit()
11
.data(jsonObject)
12
.build()
13
.apply()
14
.subscribe()
Copied!
To delete a message, simply call delete()on AmityMessage
1
message.delete().subscribe()
Copied!

Message Reaction

Reaction Data

There are 3 Reaction related methods on AmityMessage:
1. getReactionCount() returns Int, the total reaction count on the message.
2. getMyReactions() returns List<String>, a collection of reactions that have been added by the active user.
3. getReactions() returns AmityReactionMap, an extension of Map<String, Int> of reaction name and its count.
1
// To get reaction count of the message
2
val reactionCount = message.getReactionCount()
3
4
// To get a list of my reactions on the message
5
val myReactions = message.getMyReactions()
6
7
// To get a reaction map of the messsage
8
val reactionMap = message.getReactionMap()
9
10
// Count of a specific reactionName
11
val likeCount = reactionMap.getCount("like")
Copied!

Add/Remove Reaction

You can choose to add/remove a reaction to/from a message using the react() method on AmityMessage.
1
// Add a reaction
2
message.react()
3
.addReaction("like")
4
.subscribe()
5
6
// Remove a reaction
7
message.react()
8
.removeReaction("like")
9
.subscribe()
Copied!

Reaction Query

To query for a list of all reactions on a message:
1
message.getReactions()
2
.build()
3
.query()
4
.subscribe( { adapter.submitList(it) } )
Copied!
This method will return a Flowable<PagedList<AmityReaction>> of all reactions in the specified message.

Mention

The mention feature in chat can be used to call attention to specific members. By @mentioning their names, this will promptly notify them so you can get a faster response to your message. Mention is only supported on TEXT message type.
Mention is supported in these channel types:
    Community
    Live
Mention is not supported in the Conversation channel type.

Create a message with mentions

There are 2 types of mentions:
    1.
    USER - When using mention of this type, a specific member being mentioned will receive a push notification. Up to 30 channel members can be mentioned per message.
1
// mention specific users
2
val mentionees = mutableListOf<AmityMentionee>()
3
mentionees.add(AmityMentionee.USER("userId"))
4
5
AmityChatClient
6
.newMessageRepository()
7
.createMessage("channelId")
8
.with()
9
.text("text")
10
.metadata(AmityMentionMetadataCreator(mentionMetaData).create())
11
.mention(mentionees)
12
.build()
13
.send()
Copied!
2. CHANNEL - When using mention of this type, all channel members will receive push notification.
1
// mention the whole channel
2
val mentionees = mutableListOf<AmityMentionee>()
3
mentionees.add(AmityMentionee.CHANNEL)
4
5
AmityChatClient
6
.newMessageRepository()
7
.createMessage("channelId")
8
.with()
9
.text("text")
10
.metadata(AmityMentionMetadataCreator(mentionMetaData).create())
11
.mention(mentionees)
12
.build()
13
.send()
Copied!

Banned users cannot be mentioned. However, admins can still find the banned users' names in the suggestion list during search when composing the message. But once the message is sent, the banned users’ information will not be included in the message payload anymore.

Viewing mentions

Mentionee list provided during message creation is available with AmityMessage
1
val mentionees : List<AmityMentionee> = message.getMentionees()
Copied!
SDK only sends push notifications according to the mentionee list. The system doesn't handle any data related to UI rendering.
However, AmityMessage has a property called metadata which can hold an object and will not be tampered by the system. So, it can be utilized as a storage for UI-related data.

UI helper

SDK provides a helper which helps in generating a metadata containing sufficient data to highlight text with indices.
1
// example
2
val text = "@Channel @UserA hello!"
3
val mentionees = mutableListOf<AmityMentionee>()
4
mentionees.add(AmityMentionee.CHANNEL)
5
mentionees.add(AmityMentionee.USER("userId"))
6
7
val mentionMetaData = mutableListOf<AmityMentionMetadata>()
8
// add channel mention as metadata
9
mentionMetaData.add(AmityMentionMetadata.CHANNEL(0, 7))
10
// add users mention as metadata
11
mentionMetaData.add(AmityMentionMetadata.USER("userId", 9, 5))
12
13
// generating metadata with the helper
14
val mentionMetadataJsonObject = AmityMentionMetadataCreator(mentionMetaData).create()
15
16
AmityChatClient
17
.newMessageRepository()
18
.createMessage("channelId")
19
.with()
20
.text(text)
21
.mention(mentionees)
22
.metadata(mentionMetadataJsonObject)
23
.build()
24
.send()
Copied!
SDK also provides another helper in parsing UI mention data from metadata.
1
// parse metadata with the helper
2
val mentionedGetter = AmityMentionMetadataGetter(message.getMetadata())
3
val mentionedUsers = mentionedGetter.getMentionedUsers()
4
val mentionedChannels = mentionGetter.getMentionedChannels()
5
6
// highlight text with indices
7
mentionedUsers.forEach { mentionedUser ->
8
// get started index of mentioned user
9
mentionedUser.getIndex()
10
// get length of mentioned user
11
mentionedUser.getLength()
12
}
13
14
mentionedChannels.forEach { mentionedChannel ->
15
// get started index of mentioned channel
16
mentionedChannel.getIndex()
17
// get length of mentioned channel
18
mentionedChannel.getLength()
19
}
Copied!

Mentioning banned users

Banned users will not receive any push notifications if they are being mentioned. When searching, only the admins can search for the banned users to mention. If users who are not admins will search the banned users, they will get an error. Here is a sample method for admins in searching banned users:
1
AmityChatClient.newChannelRepository()
2
.membership("channelId")
3
.searchMembers("keyword")
4
.membershipFilter(listOf(AmityChannelMembership.BANNED, AmityChannelMembership.MEMBER))
5
.build()
6
.getPagingData()
7
.subscribe()
Copied!

Mention notifications

When users are being mentioned, they will receive push notifications. You can customize the push notification content such as the title and the body using the notification setting API.
Provide the notification title and body in the titleTemplate and bodyTemplate parameters respectively. Here is a sample model:
1
{
2
"level": "network",
3
"isPushNotifiable": true,
4
"notifiableEvents": [
5
{
6
"name": "text-mention-message.created",
7
"isPushNotifiable": true,
8
"titleTemplate": "{{UserDisplayName}} mentioned you in {{ChannelName}}",
9
"bodyTemplate": "{{Message}}"
10
}
11
]
12
}
Copied!
Last modified 5d ago