Moderation Tools

LAST EDIT Apr 20 2021

Flag

Copied!

Any user is allowed to flag a message. Flagging does not perform any particular action on the chat. The API will only trigger the related webhook event and make the message appear on your Dashboard Chat Moderation view.

1
const flag = await client.flagMessage(messageResponse.message.id);
1
$flag = $client->flagMessage('message-id', ['user_id' => 'user-id']);
1
await client.flagMessage("messageID");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import StreamChat 
 
// FLAG/UNFLAG MESSAGES 
 
/// 1: Create a `ChannelId` that represents the channel you want to get a message from. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Create a `MessageId` that represents the message you want to flag. 
let messageId = "message-id" 
 
/// 3: Use the `ChatClient` to create a `ChatMessageController` with the `ChannelId` and message id. 
let messageController = chatClient.messageController(cid: channelId, messageId: messageId) 
 
/// 4: Call `ChatMessageController.flag` to flag the message. 
messageController.flag { error in 
    // handle possible errors / access message 
    print(error ?? "message flagged") 
     
    /// 5: Call `ChatMessageController.unflag` to unflag the message. 
    messageController.unflag { error in 
        print(error ?? "message unflagged") 
    } 
} 
 
// FLAG/UNFLAG USERS 
 
let userController = chatClient.userController(userId: "another_user") 
 
/// 1: Call `ChatUserController.flag` to flag the user. 
userController.flag { error in 
    // handle possible errors 
    print(error ?? "user flagged") 
     
    /// 2: Call `ChatUserController.unflag` to unflag the user. 
    userController.unflag { error in 
        print(error ?? "user unflagged") 
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client.flagMessage("message-id").enqueue(result -> { 
    if (result.isSuccess()) { 
        // Message was flagged 
        Flag flag = result.data(); 
    } else { 
        // Handle result.error() 
     } 
}); 
 
client.flagUser("user-id").enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was flagged 
        Flag flag = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client.flagMessage("message-id").enqueue { result -> 
    if (result.isSuccess) { 
        // Message was flagged 
        val flag: Flag = result.data() 
    } else { 
        // Handle result.error() 
    } 
} 
 
client.flagUser("user-id").enqueue { result -> 
    if (result.isSuccess) { 
        // User was flagged 
        val flag: Flag = result.data() 
    } else { 
        // Handle result.error() 
    } 
}

Mutes

Copied!

Any user is allowed to mute another user. Mutes are stored at user level and returned with the rest of the user information when connectUser is called. A user will be be muted until the user is unmuted or the mute is expired.

nametypedescriptiondefaultoptional
timeoutnumberThe timeout in minutes until the mute is expired.no limit
1
2
3
4
5
6
7
8
9
10
// client-side mute 
const mute = await client.muteUser('eviluser'); 
 
// client-side mute for 60 minutes 
const mute = await client.muteUser('eviluser', null, { 
    timeout: 60 
}); 
 
// server-side mute requires the id of the user creating the mute as well 
const mute = await client.muteUser('eviluser', 'user_id');
1
2
// server-side mute requires the id of the user creating the mute as well 
$mute = $client->muteUser('evil-user', 'user-id');
1
2
3
await client.muteUser("eviluser"); 
 
await client.unmuteUser("eviluser");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import StreamChat 
 
let userController = chatClient.userController(userId: "another_user") 
 
/// 1: Call `ChatUserController.mute` to mute the user. 
userController.mute { error in 
    // handle possible errors 
    print(error ?? "user muted") 
     
    /// 2: Call `ChatUserController.unmute` to unmute the user. 
    userController.unmute { error in 
        print(error ?? "user unmuted") 
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
client.muteUser("user-id").enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was muted 
        Mute mute = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
client.unmuteUser("user-id").enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was unmuted 
    } else { 
        // Handle result.error() 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
client.muteUser("user-id").enqueue { result -> 
    if (result.isSuccess) { 
        // User was muted 
        val mute: Mute = result.data() 
    } else { 
        // Handle result.error() 
    } 
} 
 
client.unmuteUser("user-id").enqueue { result -> 
    if (result.isSuccess) { 
        // User was unmuted 
    } else { 
        // Handle result.error() 
    } 
}

After muting a user messages will still be delivered via web-socket. Implementing business logic such as hiding messages from muted users or display them differently is left to the developer to implement.

Messages from muted users are not delivered via push (APN/Firebase)

Ban

Copied!

Users can be banned from an app entirely or from a channel. When a user is banned, it will not be allowed to post messages until the ban is removed or expired but it will be able to connect to Chat and to channels as before.

It is also possible to ban the user's last known IP address to prevent creation of new "throw-away" accounts. This type of ban is only applicable on the app level. We do not recommend applying IP ban without reasonable timeout, however this is not restricted. The IP address will be unbanned either after reaching a timeout or with explicit user unban.

In most cases only admins or moderators are allowed to ban other users from a channel.

nametypedescriptiondefaultoptional
timeoutnumberThe timeout in minutes until the ban is automatically expired.no limit
reasonstringThe reason that the ban was created.
ip_banbooleanWhether or not to apply IP address banfalse
banned_by_idstringThe ID of the user who is performing the ban. This is required only when using API from the server-side
Banning a user from all channels can only be done using server-side auth.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ban a user for 60 minutes from all channel 
let data = await client.banUser('eviluser', { 
    banned_by_id: userID, // ID of the user who is performing the ban (Server-side auth) 
    timeout: 60, 
    reason: 'Banned for one hour', 
}); 
 
// ban a user and their IP address for 24 hours 
let data = await client.banUser('eviluser', { 
   banned_by_id: userID, 
   timeout: 24*60, 
   ip_ban:  true, 
   reason: 'Please come back tomorrow', 
}); 
 
// ban a user from the livestream:fortnite channel 
data = await channel.banUser('eviluser', { 
    banned_by_id: userID, 
    reason: 'Profanity is not allowed here', 
}); 
 
// remove ban from channel 
data = await channel.unbanUser('eviluser'); 
 
// remove global ban 
data = await authClient.unbanUser('eviluser');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ban a user for 60 minutes from all channel 
client.ban_user("eviluser", banned_by_id=admin_id, timeout=60, reason="Banned for one hour") 
 
# ban a user and their IP address for 24 hours 
client.ban_user("eviluser", banned_by_id=admin_id, timeout=24*60, ip_ban=true, reason="Please come back tomorrow") 
 
# ban a user from the livestream:fortnite channel 
channel.ban_user("eviluser", banned_by_id=admin_id, reason="Profanity is not allowed here") 
 
# remove ban from channel 
channel.unban_user("eviluser") 
 
# remove global ban 
client.unban_user("eviluser")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Ban user for 60 minutes from a channel 
channelClient.banUser("user-id", "Bad words", 60).enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was banned 
    } else { 
        // Handle result.error() 
    } 
}); 
 
channelClient.unBanUser("user-id").enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was unbanned 
    } else { 
        // Handle result.error() 
    } 
 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import StreamChat 
 
/// 1: Create a `ChannelId` that represents the channel you want to ban a user from. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Create a `ChatChannelMemberController` to make operations on a channel member. 
let controller = chatClient.memberController(userId: "another_user", in: channelId) 
 
/// 3: Call `ChatChannelMemberController.ban` to ban the user from the channel. 
controller.ban { error in 
    // handle possible errors 
    print(error ?? "user banned") 
     
    /// 4: Call `ChatUserController.unmute` to unmute the user. 
    controller.unban { error in 
        print(error ?? "user unmuted") 
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Ban user for 60 minutes from a channel 
channelClient.banUser(targetId = "user-id", reason = "Bad words", timeout = 60).enqueue { result -> 
    if (result.isSuccess) { 
        // User was banned 
    } else { 
        // Handle result.error() 
    } 
} 
 
channelClient.unBanUser(targetId = "user-id").enqueue { result -> 
    if (result.isSuccess) { 
        // User was unbanned 
    } else { 
        // Handle result.error() 
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ban a user for 60 minutes from all channel 
$ban = $client->banUser('evil-user', [ 
    'timeout' => 60, 
    'reason' => 'Banned for one hour', 
    'banned_by_id' => 'user-id' 
]); 
 
// ban a user and their IP address for 24 hours 
$ban = $channel->banUser('evil-user', [ 
    'banned_by_id' => 'user-id' 
    'timeout' => 24*60, 
    'ip_ban' => true, 
    'reason' => 'Please come back tomorrow', 
]); 
 
// ban a user from the livestream:fortnite channel 
$ban = $channel->banUser('evil-user', [ 
    'reason' => 'Banned for one hour', 
    'banned_by_id' => 'user-id' 
]); 
 
// remove ban from channel 
$unban = $channel->unbanUser('evil-user'); 
 
// remove global ban 
$unban = $client->unbanUser('evil-user');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ban a user for 60 minutes from all channel 
await client.banUser('eviluser', { 
    'banned_by_id': userID, // ID of the user who is performing the ban (Server-side auth) 
    'timeout': 60, 
    'reason': 'Banned for one hour', 
}); 
 
// ban a user and their IP address for 24 hours 
 await client.banUser('eviluser', { 
   'banned_by_id': userID, 
   'timeout': 24*60, 
   'ip_ban':  true, 
   'reason': 'Please come back tomorrow', 
}); 
 
// ban a user from the livestream:fortnite channel 
await channel.banUser('eviluser', { 
   'banned_by_id': userID, 
    'reason': 'Profanity is not allowed here', 
}); 
 
// remove ban from channel 
await channel.unbanUser('eviluser'); 
 
// remove global ban 
await authClient.unbanUser('eviluser');

Query Banned Users

Copied!

Banned users can be retrieved in different ways:

  1. Using the dedicated query bans endpoint

  2. User Search: you can add the banned:true condition to your search. Please note that this will only return users that were banned at the app-level and not the ones that were banned only on channels.

1
2
3
4
5
// retrieve the list of banned users 
const response = await client.queryUsers({banned:true}, {}, {limit:10, offset:0}); 
 
// query for banned members from one channel 
const results = await client.queryBannedUsers({channel_cid: "livestream:123"});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Retrieve the list of banned users 
client.queryUsers( 
    QueryUsersRequest( 
        filter = Filters.eq("banned", true), 
        offset = 0, 
        limit = 10, 
    ) 
).enqueue { result -> 
    if (result.isSuccess) { 
       val users: List<User> = result.data() 
   } else { 
       // Handle result.error() 
   } 
} 
 
// Query for banned members from one channel 
client.queryBannedUsers(filter = Filters.eq("channel_cid", "ChannelType:ChannelId")).enqueue { result -> 
    if (result.isSuccess) { 
        val bannedUsers: List<BannedUser> = result.data() 
    } else { 
        // Handle result.error() 
    } 
}
1
2
3
4
5
// retrieve the list of banned users 
await client.queryUsers(filter: { 
'banned': true, 
},  
PaginationParams(limit:10, offset:0));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Retrieve the list of banned users 
FilterObject filter = Filters.eq("banned", true); 
QueryUsersRequest request = new QueryUsersRequest(filter, 0, 10); 
client.queryUsers(request).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<User> users = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
// Query for banned members from one channel 
FilterObject channelFilter = Filters.eq("channel_cid", "ChannelType:ChannelId"); 
client.queryBannedUsers(channelFilter).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<BannedUser> bannedUsers = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});

Query Bans Endpoint

Copied!

The query bans endpoint allows you to list bans for your application. Similar to other query endpoints, you can filter bans by different fields and control the ordering of results.

Globally banned users can only be retrieved using server-side auth

Available fields

Copied!

Name

Description

Example

Operators

channel_cid

The channel CID for the ban. When this parameter is not provided, both global and channel bans will be returned.

{ channel_cid :{$in:["livestream:1","livestream:2"]}}

$eq, $in

user_id

The ID of the banned user

{ user_id: "evil_user" }

$eq, $in, $neq, $nin

expired

The date (RFC339) of the ban expiration

{ expired: {$gt: "2020-10-02T15:00:00Z"} }

$eq, $gt, $gte, $lt, $lte

created_at

The date (RFC339) of the ban creation

{ created_at: {$gt: "2020-10-02T15:00:00Z"} }

$eq, $gt, $gte, $lt, $lte

banned_by_id

The ID of the user that created the ban

{ banned_by_id: "42"}

$eq, $in, $neq, $nin

Pagination for bans can be done in two ways: using offset/limit or using the "created_at" field. Bans are returned in ascending order by default so to get the second page you need to request bans with "created_at" less than the created_at of the last ban in the first page. Ordering can be reversed using the sort option.

1
2
3
4
5
6
7
8
// get the bans for channel livestream:123 in descending order 
const response = await client.queryBannedUsers({channel_cid: "livestream:123"}, {created_at: -1}); 
 
// get the next page of bans for the same channel 
await client.queryBannedUsers( 
   { channel_cid: "livestream:123", created_at_before_or_equal: createdAt }, 
   { created_at: -1 } 
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Get the bans for channel livestream:123 in descending order 
client.queryBannedUsers( 
    filter = Filters.eq("channel_cid", "livestream:123"), 
    sort = QuerySort.desc(BannedUsersSort::createdAt), 
).enqueue { result -> 
    if (result.isSuccess) { 
        val bannedUsers: List<BannedUser> = result.data() 
    } else { 
        // Handle result.error() 
   } 
} 
 
// Get the page of bans which where created before or equal date for the same channel 
client.queryBannedUsers( 
    filter = Filters.eq("channel_cid", "livestream:123"), 
    sort = QuerySort.desc(BannedUsersSort::createdAt), 
    createdAtBeforeOrEqual = Date(), 
).enqueue { result -> 
    if (result.isSuccess) { 
        val bannedUsers: List<BannedUser> = result.data() 
    } else { 
        // Handle result.error() 
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Get the bans for channel livestream:123 in descending order 
FilterObject filter = Filters.eq("channel_cid", "livestream:123"); 
QuerySort<BannedUsersSort> sort = new QuerySort<BannedUsersSort>().desc("created_at"); 
client.queryBannedUsers(filter, sort).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<BannedUser> bannedUsers = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
// Get the page of bans which where created before or equal date for the same channel 
client.queryBannedUsers(filter, sort, null, null, null, null, null, new Date()).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<BannedUser> bannedUsers = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});

Shadow Ban

Copied!

Users can be shadow banned from an app entirely or from a channel. When a user is shadow banned, they will still be allowed to post messages, but any message sent during, will have the shadowed: true field set. However, this will be invisible from the author of the message.

It's up to the client-side implementation to hide or otherwise handle these messages appropriately.
nametypedescriptiondefaultoptional
banned_by_idstringThe ID of the user who is performing the ban. This is required only when using API from the server-side-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// shadow ban a user from all channels 
let data = await client.shadowBan('eviluser'); 
 
// shadow ban a user from a channel 
data = await channel.shadowBan('eviluser'); 
 
// remove shadow ban from channel 
data = await channel.removeShadowBan('eviluser'); 
 
// remove global shadow ban 
data = await client.removeShadowBan('eviluser'); 
 
// server-side shadow ban 
data = await client.shadowBan('eviluser', { banned_by_id: 'regularUser' });
1
2
3
4
5
6
7
8
9
10
11
// shadow ban a user from all channels 
await client.shadowBan('eviluser'); 
 
// shadow ban a user from a channel 
await channel.shadowBan('eviluser'); 
 
// remove shadow ban from channel 
await channel.removeShadowBan('eviluser'); 
 
// remove global shadow ban 
await client.removeShadowBan('eviluser');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Shadow ban user for 60 minutes from a channel 
channelClient.shadowBanUser("user-id", "Bad words", 60).enqueue(result -> { 
    if (result.isSuccess()) { 
        // User was shadow banned 
    } else { 
        // Handle result.error() 
    } 
}); 
 
channelClient.removeShadowBan("user-id").enqueue(result -> { 
    if (result.isSuccess()) { 
         // Shadow ban was removed 
    } else { 
         // Handle result.error() 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Shadow ban user for 60 minutes from a channel 
channelClient.shadowBanUser(targetId = "user-id", reason = "Bad words", timeout = 60).enqueue { result -> 
     if (result.isSuccess) { 
         // User was shadow banned 
     } else { 
         // Handle result.error() 
     } 
} 
 
channelClient.removeShadowBan("user-id").enqueue { result -> 
    if (result.isSuccess) { 
        // Shadow ban was removed 
    } else { 
        // Handle result.error() 
    } 
}

Administrators can view shadow banned user status in queryChannels(), queryMembers() and queryUsers().

Block Lists

Copied!

A list of words you can define to moderate chat messages. A block list can be assigned to each channel type to either block or flag messages that contain these words. More information can be found here.

Advanced Chat Moderation

Copied!
Advanced Chat Moderation is currently in beta and accepting trial candidates. Please contact support to discuss your options.

An AI-based classification system to detect various types of bad content. The tool is powered by a machine learning model that provices a confidence interval (0-1) for a message, in each of three categories: Spam, Explicit and Toxic. The model is highly configurable for each channel type and removes the manual work of a human moderator. You can learn more here.