Moderate misbehaving users

Requires App Context

To work with stored user metadata, you must enable App Context for your app's keyset in the Admin Portal.

Chat SDK provides moderation mechanisms for:

  • Admins to mute misbehaving users on channels.
  • Admins to ban misbehaving users from accessing channels.

For example, an admin can mute or ban the users from a given channel using Access Manager.

Mute or ban users

As an admin, you can mute a specific user on a channel or ban them from accessing that channel using three setRestrictions() methods.

All of them give the same output. The only difference is that you call a given method on the Chat, User, or the Channel object. Depending on the object, these methods take a different set of input parameters.

When an admin mutes or bans a user on a channel, a moderation event is created (of type muted or banned). You can listen to these events and, for example, remove user's membership on that channel.

Also, when an admin mutes or bans a user, an additional moderation membership is created for that user. This membership copies the ID of the channel and adds the PUBNUB_INTERNAL_MODERATION_ prefix to it, even though no new channel gets created for that purpose. This moderation membership stores information about the user's current mute and ban restrictions under the custom property.

The reason behind creating an additional moderation membership was to have an object that could be secured with Access Manager and made inaccessible to users. The standard membership object couldn't serve this purpose as it stores info on the users' lastReadMessageTimetoken custom data that users should access to be able to see unread messages on channels.

The additional membership is created only for the moderation purposes - when fetching all channel memberships for a given user with the getMemberships() method, you won't see the moderation membership as Chat SDK filters it out automatically with App Context Filtering Language.

When you lift restrictions on the user (unmute or unban them), the moderation membership is removed and a moderation event of type lifted is created.

To learn if a user is muted on a given channel or banned, use the Chat SDK methods to check moderation restrictions.

Requires Secret Key authentication

Mute and ban restrictions for the client devices should be set by servers initializing Chat SDK with a Secret Key (available on the Admin Portal on your app's keyset).

The secretKey should only be used within a secure server and never exposed to client devices. If the secretKey is ever compromised, it can be an extreme security risk to your application. If you suspect your secretKey has been compromised, you can generate a new secretKey for the existing PubNub keyset on the Admin Portal.

Method signature

icon

Under the hood


These methods take the following parameters:

  • setRestrictions() (on the Chat object)

    chat.setRestrictions(
    userId: string,
    channelId: string,
    {
    ban?: boolean,
    mute?: boolean,
    reason?: string
    }
    ): Promise<void>
  • setRestrictions() (on the User object)

    user.setRestrictions(
    channel: Channel,
    {
    ban?: boolean,
    mute?: boolean,
    reason?: string
    }
    ): Promise<void>
  • setRestrictions() (on the Channel object)

    channel.setRestrictions(
    user: User,
    {
    ban?: boolean,
    mute?: boolean,
    reason?: string
    }
    ): Promise<void>

Input

ParameterTypeRequired for ChatRequired for UserRequired for ChannelDefaultDescription
userIdstringYesNoNon/aUnique User ID that becomes your app's current user. It's a string of up to 92 characters that identifies a single client (end user, device, or server) that connects to PubNub. Based on User ID, PubNub calculates pricing for your apps' usage. User ID should be persisted and remain unchanged. If you don't set userId, you won't be able to connect to PubNub. In this method, userId stands for the user that you want to mute or ban.
channelIdstringYesNoNon/aID of the channel on/from which the user should be muted or banned.
channelChannelNoYesNon/aChannel object on/from which the user should be muted or banned.
userUserNoNoYesn/aUser object to be muted or banned.
 → banbooleanNoNoNon/aValue that represents the user's moderation restrictions. Set to true to ban the user from the channel or to false to unban them.
 → mutebooleanNoNoNon/aValue that represents the user's moderation restrictions. Set to true to mute the user on the channel or to false to unmute them.
 → reasonstringNoNoNon/aReason why you want to ban or mute the user.

Output

TypeDescription
Promise<void>Method returns no output data.

Errors

Whenever you try to mute or ban a user on a client that was not initialized with a Secret Key, you'll get the Moderation restrictions can only be set by clients initialized with a Secret Key error.

Basic usage

Mute

Mute support_agent_15 on the support channel.

  • setRestrictions() (on the Chat object)

    await chat.setRestrictions(
    userId: "support_agent_15",
    channelId: "support",
    {
    mute: true
    }
    )
  • setRestrictions() (on the User object)

    // reference "support-agent-15"
    const user = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const support = await chat.getChannel("support")

    // mute the user
    await user.setRestrictions(
    channel: support,
    {
    mute: true
    }
    )
  • setRestrictions() (on the Channel object)

    // reference "support-agent-15"
    const mutedUser = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const channel = await chat.getChannel("support")

    // mute the user
    channel.setRestrictions(
    user: mutedUser,
    {
    mute: true
    }
    )

Ban

Ban support_agent_15 from the support channel.

  • setRestrictions() (on the Chat object)

    // reference "support-agent-15"
    const user = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const channel = await chat.getChannel("support")

    // ban the user
    await chat.setRestrictions(
    userId: "support_agent_15",
    channelId: "support",
    {
    ban: true
    }
    )
  • setRestrictions() (on the User object)

    // reference "support-agent-15"
    const user = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const support = await chat.getChannel("support")

    // ban the user
    await user.setRestrictions(
    channel: support,
    {
    ban: true
    }
    )
  • setRestrictions() (on the Channel object)

    // reference "support-agent-15"
    const mutedUser = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const channel = await chat.getChannel("support")

    // ban the user
    channel.setRestrictions(
    user: mutedUser,
    {
    ban: true
    }
    )

Check restrictions

One user on one channel

Check if there are any mute or ban restrictions set for a user on one channel using the getChannelRestrictions() and getUserRestrictions() methods.

icon

Under the hood

Method signature

These methods take the following parameters:

  • getChannelRestrictions()

    user.getChannelRestrictions(
    channel: Channel
    ): Promise<{
    ban: boolean,
    mute: boolean,
    reason: string | number | boolean,
    }>
  • getUserRestrictions()

    channel.getUserRestrictions(
    user: User
    ): Promise<{
    ban: boolean;
    mute: boolean;
    reason: string | number | boolean,
    }>
Input
ParameterTypeRequired in getChannelRestrictions()Required in getUserRestrictions()DefaultDescription
channelChannelYesNon/aChannel object on/from which the user can be muted or banned.
userUserNoYesn/aUser object that can be muted or banned.
Output
ParameterTypeDescription
Promise<>objectReturned object containing two fields: ban and mute.
 → banbooleanInfo whether the user is banned from the channel.
 → mutebooleanInfo whether the user is muted on the channel.
 → reasonstring, number, or booleanReason why the user was banned or muted.

Basic usage

Check if the user support_agent_15 has any restrictions set on the support channel.

  • getChannelRestrictions()

    // reference "support-agent-15"
    const user = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const support = await chat.getChannel("support")

    // check user restrictions
    await user.getChannelRestrictions(
    channel: support
    )
  • getUserRestrictions()

    // reference "support-agent-15"
    const restrictedUser = await chat.getUser("support_agent_15")

    // reference the "support" channel
    const channel = await chat.getChannel("support")

    // check user restrictions
    await channel.getUserRestrictions(
    user: restrictedUser
    )

One user on all channels

Check if there are any mute or ban restrictions set for a user on all channels they are a member of using the getChannelsRestrictions() method.

icon

Under the hood

Method signature

This method takes the following parameters:

user.getChannelsRestrictions(
{
limit?: number,
page?: {
next?: string,
prev?: string
},
sort?: object
}
): Promise<{
page: {
next: string,
prev: string,
},
total: number,
show all 23 lines
Input
ParameterTypeRequiredDefaultDescription
limitnumberNo100Number of objects to return in response. The default (and maximum) value is 100.
pageobjectNon/aObject used for pagination to define which previous or next result page you want to fetch.
 → nextstringNon/aRandom string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
 → prevstringNon/aRandom string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the next parameter is supplied.
sortobjectNon/aKey-value pair of a property to sort by, and a sort direction. Available options are id, name, and updated. Use asc or desc to specify the sorting direction, or specify null to take the default sorting direction (ascending). For example: {name: "asc"}. By default, the items are sorted by the last updated date.
Output
ParameterTypeDescription
Promise<>objectReturned object containing these fields: page, total, status, and restrictions.
 → pageobjectObject used for pagination to define which previous or next result page you want to fetch.
   → nextstringRandom string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
   → prevstringRandom string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the next parameter is supplied.
 → totalnumberTotal number of restrictions.
 → statusnumberStatus code of a server response, like 200.
 → restrictionsobjectObject containing a list of restrictions.
   → banbooleanInfo whether the user is banned from the given channel.
   → mutebooleanInfo whether the user is muted on the given channel.
   → reasonstring, number, or booleanReason why the user was banned or muted.
   → channelIdstringID of the channel containing user restrictions.

Basic usage

List all mute and ban restrictions set for the user support_agent_15.

// reference "support-agent-15"
const user = await chat.getUser("support_agent_15")

// list all restrictions set for that user
await user.getChannelsRestrictions()

All users on one channel

Check if there are any mute or ban restrictions set for members of a given channel using the getUsersRestrictions() method.

icon

Under the hood

Method signature

This method takes the following parameters:

channel.getUsersRestrictions(
{
limit?: number,
page?: {
next?: string,
prev?: string
},
sort?: object
}
): Promise<{
page: {
next: string,
prev: string,
},
total: number,
show all 22 lines
Input
ParameterTypeRequiredDefaultDescription
limitnumberNo100Number of objects to return in response. The default (and maximum) value is 100.
pageobjectNon/aObject used for pagination to define which previous or next result page you want to fetch.
 → nextstringNon/aRandom string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
 → prevstringNon/aRandom string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the next parameter is supplied.
sortobjectNon/aKey-value pair of a property to sort by, and a sort direction. Available options are id, name, and updated. Use asc or desc to specify the sorting direction, or specify null to take the default sorting direction (ascending). For example: {name: "asc"}. By default, the items are sorted by the last updated date.
Output
ParameterTypeDescription
Promise<>objectReturned object containing these fields: page, total, status, and restrictions.
 → pageobjectObject used for pagination to define which previous or next result page you want to fetch.
   → nextstringRandom string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
   → prevstringRandom string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the next parameter is supplied.
 → totalnumberTotal number of restrictions.
 → statusnumberStatus code of a server response, like 200.
 → restrictionsobjectObject containing a list of restrictions.
   → banbooleanInfo whether the user is banned from the given channel.
   → mutebooleanInfo whether the user is muted on the given channel.
   → userIdstringID of the restricted user.

Basic usage

List all mute and ban restrictions set for the support channel.

// reference the "support" channel
const channel = await chat.getChannel("support")

// list all restrictions on the "support" channel
await channel.getUsersRestrictions()

Secure moderation

You could try to use only the banning or muting restrictions on client devices and enforce some UI moderation, like not displaying channels for users who are banned from them, or not displaying input field for users who are muted on a given channel so that they couldn't post a message.

Still, such solely client-side restrictions can easily be bypassed if not secured with an additional server-side logic that uses Access Manager to allow or block user's access to PubNub resources (channels and users). This server-side can also be followed by additional client-side errors that inform app users about their restrictions up front.

Server-side restrictions

It's recommended to use Access Manager alongside the Chat SDK methods and grant or revoke permissions from users based on their muting or banning restrictions.

For example, you could have a UI moderation dashboard (like Channel Monitor) where admins set restrictions on users by muting or banning them from specific channels. After that, you can use one of the Chat SDK methods to get moderation restrictions for users and, based on results, call the Access Manager API to either generate or revoke grant tokens for PubNub resources (channels or users).

Let's look at sample steps that use Chat SDK methods to configure a chat app, set up server permissions, listen to any permission changes in Channel Monitor UI, and invoke access grant or revoke request on the server side.

  1. Enable Access Manager.

    Navigate to your app's keyset in the Admin Portal and turn on the ACCESS MANAGER option.

  2. Initialize Chat SDK with authKey.

    On the frontend of your app, initialize the Chat SDK (init()) with the authentication key (authKey) on your clients. Use it for all requests made to PubNub APIs to authenticate users in your application and grant them access to PubNub resources (other users' metadata and channels).

    import { Chat } from "@pubnub/chat"

    const userId = "your-user-id"
    const authToken = "token-from-your-server"
    ...

    const chat = Chat.init(
    subscribeKey: "your-subscribe-key-from-admin-portal",
    publishKey: "your-publish-key-from-admin-portal"
    userId: userId,
    authKey: authToken
    )
  3. Secure backend initialization.

    On the backend, initialize the Chat SDK with the secret key (secretKey) on your servers to secure your PubNub instance.

    Secret key

    secretKey is a secret shared between your application's server and PubNub and it's used to administer Access Manager permissions for your client applications by signing and verifying the authenticity of messages and requests. Remember to never expose the secretKey to client devices.

    import { Chat } from "@pubnub/chat"

    const serverId = "auth-server"

    export const chat = await Chat.init({
    subscribeKey: "your-subscribe-key-from-admin-portal",
    publishKey: "your-publish-key-from-admin-portal",
    secretKey: "your-secret-key-from-admin-portal",
    userId: serverId,
    })
  4. Get user permissions.

    Retrieve detailed user restrictions and convert these details into a simplified permission format where each channel is marked with whether the user can read, write, or access it, based on such restrictions as bans or mutes.

    async function defineUserPermissions(userId: string) {
    // Retrieve user information and channel restrictions
    const user = await chat.getUser(userId);
    const userRestrictions = await user.getChannelsRestrictions();

    // Simplify the detailed channel restrictions into a format suitable for the authorization token
    const reducedChannels = userRestrictions.restrictions.reduce((acc, curr) => {
    return {
    ...acc,
    [curr.channelId]: {
    read: !curr.ban,
    write: (!curr.mute && !curr.ban),
    get: true,
    }
    };
    show all 20 lines
  5. Generate authorization token.

    Generate and assign an access token reflecting the user's permissions.

    The token contains information about which channels the user can access and how (read/write), and it's configured with a specific validity period. This token serves as a key for users to interact with the application according to their permissions.

    Operation-to-permission mapping

    Read the Permissions document for a complete list of available operations that users can do with PubNub resources in apps created with the Chat SDK.

    async function generateAuthToken(userId: string, reducedChannels: Record<string, any>) {
    // Set up parameters for the authorization token
    const grantTokenParams = {
    ttl: 43200,
    authorized_uuid: userId,
    patterns: {
    channels: {
    ".*": {
    get: true,
    read: true,
    write: true,
    },
    },
    },
    resources: {
    show all 28 lines
    Set short TTLs

    You can mute or ban a user for an indefinite amount of time, but you can unmute/unban them at any time. To make sure that the permissions set with Access Manager reflect the most up-to-date muting/banning restrictions on the client-side, it's recommended to set short-lived tokens (TTLs) for grant calls (valid for seconds and minutes rather than hours or days). Alternatively, if new muting/banning restrictions are set on the frontend side of your app, you can revoke Access Manager permissions on the backend using the chat.sdk.revokeToken() method.

  6. Listen for moderation events.

    All moderation events generated by muting and banning actions are sent to a single PUBNUB_INTERNAL_MODERATION.[user_id] channel. You can listen to these events through Events & Actions in the Admin Portal.

    To do that, create a new event listener, choose Messages as event source, and configure the event listener as follows:

    Listen to moderation events

  7. Act on moderation events.

    Create a Webhook type of action in Events & Actions where you must specify the URL of the server you want to hit for authentication token changes each time a new moderation event is generated.

    Link the action to the previously created listener.

    Act on moderation events

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.

Additionally, you can read moderation restrictions set with the Chat SDK methods also on your app's frontend to let a given user know upfront that they are muted on a channel or banned from accessing a channel using popup messages, iconography, etc. For example, before a user tries to write a message on a channel where they are muted and tries to search for the input field that is either greyed out or unavailable, you can display a popup message informing them about the restriction.

To let the client application react in real-time to changes in user permissions and present this information to the users effectively, the app needs to listen to and act on changes in moderation events:

  1. Listen for moderation events.

    Set up a listener to listen for the moderation event type ("banned," "muted," or "lifted") generated when UI restrictions are added or removed.

    useEffect(() => {
    // Set up a listener for moderation events on the current user's channel
    const moderationListener = chat.listenForEvents({
    channel: chat.currentUser.id,
    type: "moderation",
    callback: handleModerationEvent,
    });

    return () => {
    moderationListener(); // Remove the moderation event listener on cleanup
    };
    }, [chat]);
  2. Act on moderation events.

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

    // Callback function to handle different moderation events
    async function handleModerationEvent(moderationPayload: Event<"moderation">) {
    const { channelId, restriction, reason } = moderationPayload.payload;

    // Handle different moderation events
    if (restriction === "banned") {
    // Revoke specific permissions for banning
    console.log(`User ${chat.currentUser.id} is banned on channel ${channelId}. Reason: ${reason || "N/A"}`);

    // Revoke access rights specific to banning logic here
    // For example, you might remove write access

    } else if (restriction === "muted") {
    // Revoke specific permissions for muting
    console.log(`User ${chat.currentUser.id} is muted on channel ${channelId}. Reason: ${reason || "N/A"}`);
    show all 29 lines
  3. Remove the event listener.

    return () => {
    moderationListener(); // Remove the moderation event listener on cleanup
    };

Listen to moderation events

As an admin of your chat app, you can use the listenForEvents() method to send notifications to the affected user when you mute or ban them on a channel, or to remove user channel membership each time they are banned.

Events documentation

To read more about the events of type moderation, refer to the Chat events documentation.

Method signature

icon

Under the hood


This method has the following parameters:

chat.listenForEvents({
channel: string;
type?: "moderation";
callback: (event: Event<"moderation">) => unknown;
}): () => void
Input
ParameterTypeRequiredDefaultDescription
channelstringYesn/aChannel where you want all new moderation events to be sent.
typestringNon/aType of events. moderation is the type defined for all events emitted when a user is muted/banned for misbehaving, or when these permissions are lifted.
callbackn/aYesn/aCallback function passed as a parameter. It defines the custom behavior to be executed whenever a moderation event type is detected on the specific channel.
Output
TypeDescription
() => voidFunction you can call to disconnect (unsubscribe) from the channel and stop receiving moderation events.

Basic usage

Send a moderation event to the muted user.

user.setRestrictions(channel, { mute: true, reason: "Bad behavior" });

(async () => {
await chat.listenForEvents({
channel: "support",
type: "moderation",
callback: (event) => {
if (event.payload.restriction === "muted") {
console.log(`You were muted on channel ${event.payload.channelId}`);
}
},
});
})();

Flag/Report users (deprecated)

DEPRECATED_report() lets you flag a misbehaving user and report them to the admin.

In each case, a user must provide a reason for flagging a given user. As a result of flagging, reported info gets published on the dedicated administrative channel (with the PUBNUB_INTERNAL_ADMIN_CHANNEL ID), and an event of the report type gets created.

icon

Under the hood

Method signature

This method takes the following parameters:

user.DEPRECATED_report(reason: string): Promise<any>

Input

ParameterTypeRequiredDefaultDescription
reasonstringYesn/aReason for reporting/flagging a given user.

Output

TypeDescription
Promise<any>Returned object with a value of any type.

Basic usage

Report support_agent_15 to the admin for posting offensive images on the support channel.

// reference the "chat" object and invoke the "getUser()" method
const user = await chat.getUser("support_agent_15")
// report "support_agent_15" to the admin and provide the reason
await user.DEPRECATED_report("Could you please mute Mike as he keeps sending offensive photos?")

Listen to report events (deprecated)

As an admin of your chat app, you can monitor all events emitted when someone reports a user for misbehaving. You can use the listenForEvents() method to create moderation dashboard alerts.

Method signature

icon

Under the hood


This method has the following parameters:

chat.listenForEvents({
channel: string;
type?: "report";
callback: (event: Event<"report">) => unknown;
}): () => void
Input
ParameterTypeRequiredDefaultDescription
channelstringYesn/aChannel to listen for new report events. Set this value to a dedicated PUBNUB_INTERNAL_ADMIN_CHANNEL where all report events are sent.
typestringNon/aType of events. report is the type defined for all events emitted when a user is flagged/reported by someone for misbehaving.
callbackn/aYesn/aCallback function passed as a parameter. It defines the custom behavior to be executed whenever a report event type is detected on the specific channel.
methodstringNon/aThis parameter is deprecated. You no longer have to provide a method used to send this event type as the method is now passed automatically.

PubNub method used to send events you listen for. Use publish for all events related to reporting.
Output
TypeDescription
() => voidFunction you can call to disconnect (unsubscribe) from the channel and stop receiving report events.

Basic usage

Notify an admin when one user reports another user.

user.DEPRECATED_report("Inappropriate content");

(async () => {
await chat.listenForEvents({
channel: "admin",
type: "report",
callback: (event) => {
console.log(`User ${event.userId} reported user ${event.payload.reported} for ${event.payload.reason}`);
},
});
})();
Last updated on