Moderate misbehaving users

Regular chat users (without admin secretKey) can hide messages from undesirable users using a mute list.

The mute list is soft-limited to 200 users and persists only for the session duration by default. Enable syncMutedUsers during initialization to persist the list (32 KiB server limit applies).

Mute list user limit

A persisted mute list can hold roughly up to 200 users (32 KiB of data). If the size of the list exceeds that limit, the Chat SDK throws an HTTP 413 error and the list won't be persisted. The users will still be muted for the duration of the session.

All events and messages originating from a muted user are still received by the client, but are ignored. The mute list affects the following means of message delivery:

Requires App Context

To mute users, you must enable App Context for your app's keyset in the Admin Portal.

Mute users as a regular chat user​

As a regular chat user, you can mute a specific user on all channels.

Method signature​

1Chat.MutedUsersManager.muteUser(userId: String): PNFuture<Unit>

Input​

* required
ParameterDescription
userId *
Type: string
User ID of the user you want to mute.

Output​

This method returns a PNFuture<Unit> that succeeds when the data has been synced with the server. If syncMutedUsers is not enabled, the PNFuture<Unit> always succeeds.

Errors​

If the size of the mute list exceeds 32 KiB (roughly 200 users), you'll get the HTTP 413 (Request Entity Too Large) error.

Sample code​

1import com.pubnub.chat.Chat
2import com.pubnub.chat.config.ChatConfiguration
3
4val chat = Chat.init(
5 ChatConfiguration(),
6 PNConfiguration.builder(UserId("myUniqueUserId"), subscribeKey = "demo").apply {
7 publishKey = "demo"
8 }.build()
9)
10
11val userToMute = "user_1905"
12
13chat.mutedUsersManager.muteUser(userToMute).await()

Unmute users as a regular chat user​

You can unmute a specific user on all channels. This removes the user from the mute list, allowing the client to see all messages and events originating from the user again.

Method signature​

1Chat.MutedUsersManager.unmuteUser(userId: String): PNFuture<Unit>

Input​

* required
ParameterDescription
userId *
Type: string
User ID of the user you want to unmute.

Output​

This method returns a PNFuture<Unit> that succeeds when the data has been synced with the server. If syncMutedUsers is not enabled, the PNFuture<Unit> always succeeds.

Sample code​

1import com.pubnub.chat.Chat
2import com.pubnub.chat.config.ChatConfiguration
3
4val chat = Chat.init(
5 ChatConfiguration(),
6 PNConfiguration.builder(UserId("myUniqueUserId"), subscribeKey = "demo").apply {
7 publishKey = "demo"
8 }.build()
9)
10
11val userToUnmute = "user_1905"
12
13chat.mutedUsersManager.unmuteUser(userToUnmute).await()

Check muted users​

You can check which users have been muted by inspecting the items in the mute list.

Method signature​

1Chat.MutedUsersManager.mutedUsers

Output​

This property returns a Set<String> where each String is a user ID of a muted user.

Sample code​

1import com.pubnub.chat.Chat
2import com.pubnub.chat.config.ChatConfiguration
3
4val chat = Chat.init(
5 ChatConfiguration(),
6 PNConfiguration.builder(UserId("myUniqueUserId"), subscribeKey = "demo").apply {
7 publishKey = "demo"
8 }.build()
9)
10
11val userToMute = "user_1905"
12
13// Mute the user
14chat.mutedUsersManager.muteUser(userToMute).await()
15
show all 19 lines

Persist the mute list​

By default, the mute list is only persisted for the duration of the user session. You can save and retrieve the mute list during client initialization, effectively persisting the mute list by setting the syncMutedUsers parameter to true during client initialization.

Mute list and Access Manager

If you use Access Manager for user moderation within your chat app and syncMutedUsers is enabled, you must grant the Chat SDK user the following permissions:

  • read permission to the PN_PRV.$currentUserId.mute1 channel.
  • update, delete, and get permissions for the PN_PRV.$currentUserId.mute1 user.

Make sure to change $currentUserId to the user ID of the chat user that will use the mute list functionality.

Check restrictions​

You can check if the chat administrator imposed any mute or ban restrictions for the following scenarios. Refer to the linked sections for more information.

Sample code​

Single user on a single channel​
1// reference the "chat" object and invoke the "getUser()" method
2chat.getUser("support_agent_15").async { userResult ->
3 userResult.onSuccess { user ->
4 if (user != null) {
5 // get the "support" channel
6 chat.getChannel("support").async { channelResult ->
7 channelResult.onSuccess { channel ->
8 if (channel != null) {
9 // get the channel restrictions for the user
10 user.getChannelRestrictions(channel).async { restrictionResult ->
11 restrictionResult.onSuccess { restriction ->
12 // handle the restriction object
13 }.onFailure {
14 // handle failure
15 }
show all 30 lines
All users on a single channel​
1// reference the "chat" object and invoke the "getChannel()" method
2chat.getChannel("support").async { result ->
3 result.onSuccess { channel ->
4 if (channel != null) {
5 // fetch the user restrictions for the channel
6 channel.getUsersRestrictions().async { restrictionsResult ->
7 restrictionsResult.onSuccess { getRestrictionsResponse ->
8 // process the returned restrictions
9 for (restriction in getRestrictionsResponse.restrictions) {
10 if (restriction.mute || restriction.ban) {
11 // handle the restriction object (either muted or banned)
12 println("User ${restriction.userId} is restricted in channel ${channel.id}: mute=${restriction.mute}, ban=${restriction.ban}")
13 }
14 }
15 }.onFailure {
show all 27 lines
Single user on all channels​
1// reference the "chat" object and invoke the "getUser()" method
2chat.getUser("support_agent_15").async { result ->
3 result.onSuccess { user ->
4 if (user != null) {
5 // fetch the channel restrictions for the user
6 user.getChannelsRestrictions().async { restrictionsResult ->
7 restrictionsResult.onSuccess { getRestrictionsResponse ->
8 // process the returned restrictions
9 for (restriction in getRestrictionsResponse.restrictions) {
10 if (restriction.mute || restriction.ban) {
11 // handle the restriction object (either muted or banned)
12 println("User is restricted on channel ${restriction.channelId}: mute=${restriction.mute}, ban=${restriction.ban}")
13 }
14 }
15 }.onFailure {
show all 27 lines

Secure moderation​

Client-side restrictions can be bypassed without server-side logic using Access Manager. Add client-side error handling to inform users of their restrictions.

Client-side restrictions​

Once you enable and define server-side permissions with Access Manager, you can be sure that your muting and banning restrictions are always enforced.

Read moderation restrictions on the frontend to notify users of restrictions before they attempt prohibited actions.

Listen to moderation events to react to permission changes in real-time:

  1. Listen for moderation events.

    Set up a listener using user.onRestrictionChanged() to react to restriction changes ("banned," "muted," or "lifted") in real-time.

    1val subscription = user.onRestrictionChanged { restriction ->
    2 handleModerationEvent(restriction)
    3}
    4
    5// Remove the moderation event listener on cleanup
    6// subscription.close()
  2. Act on moderation events.

    Update permissions in response to moderation events and generate new tokens if necessary.

    1fun handleModerationEvent(restriction: Restriction) {
    2 val channelId = restriction.channelId
    3 val reason = restriction.reason
    4
    5 when {
    6 restriction.ban -> {
    7 println("User ${chat.currentUser.id} is banned on channel $channelId. Reason: ${reason ?: "N/A"}")
    8 // Revoke access rights specific to banning logic here
    9 }
    10 restriction.mute -> {
    11 println("User ${chat.currentUser.id} is muted on channel $channelId. Reason: ${reason ?: "N/A"}")
    12 // Revoke access rights specific to muting logic here
    13 }
    14 else -> {
    15 println("User ${chat.currentUser.id} restrictions lifted on channel $channelId")
    show all 21 lines
  3. Remove the event listener.

    1// remember to call close when you're done
    2subscription.close()

Was this page useful?

Last updated on