Permissions
Permissions are Discord's primary feature, enabling users to customize their server's workings to their liking. Essentially, Permissions and permission overwrites tell Discord who is allowed to do what and where. Permissions can be very confusing at first, but this guide is here to explain and clarify them, so let's dive in!
Roles as bot permissions
If you want to keep your bot's permission checks simple, you might find it sufficient to check if the member executing the command has a specific role.
If you have the role ID, you can check if the .roles
Collection on a GuildMember object includes it, using .has()
. Should you not know the ID and want to check for something like a "Mod" role, you can use .some()
.
member.roles.cache.has('role-id-here');
// returns true if the member has the role
member.roles.cache.some(role => role.name === 'Mod');
// returns true if any of the member's roles is exactly named "Mod"
2
3
4
5
If you want to enhance this system slightly, you can include the guild owner by comparing the executing member's ID with interaction.guild.ownerId
.
To include permission checks like Administrator
or ManageGuild
, keep reading as we will cover Discord Permissions and all their intricacies in the following sections.
Terminology
- Permission: The ability to execute a certain action in Discord
- Overwrite: Rule on a channel to modify the permissions for a member or role
- BitField: Binary representation of Discord permissions
- Base Permissions: Permissions for roles the member has, set on the guild level
- Final Permissions: Permissions for a member or role, after all overwrites are applied
- Flag: Human readable string in PascalCase (e.g.,
KickMembers
) that refers to a position in the permission BitField. You can find a list of all valid flags on thePermissionsBitField#Flags
open in new window page
TIP
You can provide permission decimals wherever we use flag literals in this guide. If you are interested in a handy permission calculator, you can look at the "Bot" section in the Discord developer portalopen in new window.
Base permissions
Setting role permissions
Base permissions are set on roles, not the guild member itself. To change them, you access a Role object (for example via member.roles.cache.first()
or guild.roles.cache.random()
) and use the .setPermissions()
method. This is how you'd change the base permissions for the @everyone
role, for example:
const { PermissionsBitField } = require('discord.js');
guild.roles.everyone.setPermissions([PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel]);
2
3
Any permission not referenced in the flag array or bit field is not granted to the role.
TIP
Note that flag names are literal. Although ViewChannel
grants access to view multiple channels, the permission flag is still called ViewChannel
in singular form.
Creating a role with permissions
Alternatively you can provide permissions as a property of RoleCreateOptions
open in new window during role creation as an array of flag strings or a permission number:
const { PermissionsBitField } = require('discord.js');
guild.roles.create({ name: 'Mod', permissions: [PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.KickMembers] });
2
3
Checking member permissions
To know if one of a member's roles has a permission enabled, you can use the .has()
method on GuildMember#permissions
open in new window and provide a permission flag, array, or number to check for. You can also specify if you want to allow the Administrator
permission or the guild owner status to override this check with the following parameters.
const { PermissionsBitField } = require('discord.js');
if (member.permissions.has(PermissionsBitField.Flags.KickMembers)) {
console.log('This member can kick');
}
if (member.permissions.has([PermissionsBitField.Flags.KickMembers, PermissionsBitField.Flags.BanMembers])) {
console.log('This member can kick and ban');
}
if (member.permissions.has(PermissionsBitField.Flags.KickMembers, false)) {
console.log('This member can kick without allowing admin to override');
}
2
3
4
5
6
7
8
9
10
11
12
13
If you provide multiple permissions to the method, it will only return true
if all permissions you specified are granted.
TIP
You can learn more about the .has()
method here.
Channel overwrites
Permission overwrites control members' abilities for this specific channel or a set of channels if applied to a category with synchronized child channels.
As you have likely already seen in your desktop client, channel overwrites have three states:
- Explicit allow (
true
, green ✓) - Explicit deny (
false
, red X) - Default (
null
, gray /)
Adding overwrites
To add a permission overwrite for a role or guild member, you access the channel's PermissionOverwriteManager
open in new window and use the .create()
method. The first parameter is the target of the overwrite, either a Role or User object (or its respective resolvable), and the second is a PermissionOverwriteOptions
open in new window object.
Let's add an overwrite to lock everyone out of the channel. The guild ID doubles as the role id for the default role @everyone
as demonstrated below:
channel.permissionOverwrites.create(channel.guild.roles.everyone, { ViewChannel: false });
Any permission flags not specified get neither an explicit allow nor deny overwrite and will use the base permission unless another role has an explicit overwrite set.
You can also provide an array of overwrites during channel creation, as shown below:
const { ChannelType, PermissionsBitField } = require('discord.js');
guild.channels.create({
name: 'new-channel',
type: ChannelType.GuildText,
permissionOverwrites: [
{
id: interaction.guild.id,
deny: [PermissionsBitField.Flags.ViewChannel],
},
{
id: interaction.user.id,
allow: [PermissionsBitField.Flags.ViewChannel],
},
],
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Editing overwrites
To edit permission overwrites on the channel with a provided set of new overwrites, you can use the .edit()
method.
// edits overwrites to disallow everyone to view the channel
channel.permissionOverwrites.edit(guild.id, { ViewChannel: false });
// edits overwrites to allow a user to view the channel
channel.permissionOverwrites.edit(user.id, { ViewChannel: true });
2
3
4
5
Replacing overwrites
To replace all permission overwrites on the channel with a provided set of new overwrites, you can use the .set()
method. This is extremely handy if you want to copy a channel's full set of overwrites to another one, as this method also allows passing an array or Collection of PermissionOverwrites
open in new window.
// copying overwrites from another channel
channel.permissionOverwrites.set(otherChannel.permissionOverwrites.cache);
// replacing overwrites with PermissionOverwriteOptions
channel.permissionOverwrites.set([
{
id: guild.id,
deny: [PermissionsBitField.Flags.ViewChannel],
},
{
id: user.id,
allow: [PermissionsBitField.Flags.ViewChannel],
},
]);
2
3
4
5
6
7
8
9
10
11
12
13
14
Removing overwrites
To remove the overwrite for a specific member or role, you can use the .delete()
method.
// deleting the channel's overwrite for the user who interacted
channel.permissionOverwrites.delete(interaction.user.id);
2
Syncing with a category
If the permission overwrites on a channel under a category match with the parent (category), it is considered synchronized. This means that any changes in the categories overwrites will now also change the channels overwrites. Changing the child channels overwrites will not affect the parent.
To easily synchronize permissions with the parent channel, you can call the .lockPermissions()
method on the respective child channel.
if (!channel.parent) {
return console.log('This channel is not listed under a category');
}
channel.lockPermissions()
.then(() => console.log('Successfully synchronized permissions with parent channel'))
.catch(console.error);
2
3
4
5
6
7
Permissions after overwrites
There are two utility methods to easily determine the final permissions for a guild member or role in a specific channel: GuildChannel#permissionsFor()
open in new window and GuildMember#permissionsIn()
open in new window - Role#permissionsIn()
open in new window. All return a PermissionsBitField
open in new window object.
To check your bot's permissions in the channel the command was used in, you could use something like this:
// final permissions for a guild member using permissionsFor
const botPermissionsFor = channel.permissionsFor(guild.members.me);
// final permissions for a guild member using permissionsIn
const botPermissionsIn = guild.members.me.permissionsIn(channel);
// final permissions for a role
const rolePermissions = channel.permissionsFor(role);
2
3
4
5
6
7
8
WARNING
The .permissionsFor()
and .permissionsIn()
methods return a Permissions object with all permissions set if the member or role has the global Administrator
permission and does not take overwrites into consideration in this case. Using the second parameter of the .has()
method as described further down in the guide will not allow you to check without taking Administrator
into account here!
If you want to know how to work with the returned Permissions objects, keep reading as this will be our next topic.
The Permissions object
The PermissionsBitField
open in new window object is a discord.js class containing a permissions bit field and a bunch of utility methods to manipulate it easily. Remember that using these methods will not manipulate permissions, but rather create a new instance representing the changed bit field.
Displaying permission flags
discord.js provides a toArray()
method, which can be used to convert a Permissions
object into an array containing permission flags. This is useful if you want to display/list them and it enables you to use other array manipulation methods. For example:
const memberPermissions = member.permissions.toArray();
const rolePermissions = role.permissions.toArray();
// output: ['SendMessages', 'AddReactions', 'ChangeNickname', ...]
2
3
TIP
The return value of toArray()
always represents the permission flags present in the Permissions instance that the method was called on. This means that if you call the method on, for example: PermissionOverwrites#deny
, you will receive an array of all denied permissions in that overwrite.
Additionally, you can serialize the Permissions object's underlying bit field by calling .serialize()
. This returns an object that maps permission names to a boolean value, indicating whether the relevant "bit" is available in the Permissions instance.
const memberPermissions = member.permissions.serialize();
const rolePermissions = role.permissions.serialize();
/* output: {
SendMessages: true,
AddReactions: true,
BanMembers: false,
...
}
*/
2
3
4
5
6
7
8
9
Converting permission numbers
Some methods and properties in discord.js return permission decimals rather than a Permissions object, making it hard to manipulate or read them if you don't want to use bitwise operations. However, you can pass these decimals to the Permissions constructor to convert them, as shown below.
const { PermissionsBitField } = require('discord.js');
const permissions = new PermissionsBitField(268_550_160n);
2
3
You can also use this approach for other PermissionResolvable
open in new windows like flag arrays or flags.
const { PermissionsBitField } = require('discord.js');
const flags = [
PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.EmbedLinks,
PermissionsBitField.Flags.AttachFiles,
PermissionsBitField.Flags.ReadMessageHistory,
PermissionsBitField.Flags.ManageRoles,
];
const permissions = new PermissionsBitField(flags);
2
3
4
5
6
7
8
9
10
11
Checking for permissions
The Permissions object features the .has()
method, allowing an easy way to check flags in a Permissions bit field. The .has()
method takes two parameters: the first being either a permission number, single flag, or an array of permission numbers and flags, the second being a boolean, indicating if you want to allow the Administrator
permission to override (defaults to true
).
Let's say you want to know if the decimal bit field representation 268_550_160
has ManageChannels
referenced:
const { PermissionsBitField } = require('discord.js');
const bitPermissions = new PermissionsBitField(268_550_160n);
console.log(bitPermissions.has(PermissionsBitField.Flags.ManageChannels));
// output: true
console.log(bitPermissions.has([PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.EmbedLinks]));
// output: true
console.log(bitPermissions.has([PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.KickMembers]));
// output: false
const flagsPermissions = new PermissionsBitField([
PermissionsBitField.Flags.ManageChannels,
PermissionsBitField.Flags.EmbedLinks,
PermissionsBitField.Flags.AttachFiles,
PermissionsBitField.Flags.ReadMessageHistory,
PermissionsBitField.Flags.ManageRoles,
]);
console.log(flagsPermissions.has(PermissionsBitField.Flags.ManageChannels));
// output: true
console.log(flagsPermissions.has([PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.EmbedLinks]));
// output: true
console.log(flagsPermissions.has([PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.KickMembers]));
// output: false
const adminPermissions = new PermissionsBitField(PermissionsBitField.Flags.Administrator);
console.log(adminPermissions.has(PermissionsBitField.Flags.ManageChannels));
// output: true
console.log(adminPermissions.has(PermissionsBitField.Flags.ManageChannels, true));
// output: true
console.log(adminPermissions.has(PermissionsBitField.Flags.ManageChannels, false));
// output: false
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
39
40
Manipulating permissions
The Permissions object enables you to easily add or remove individual permissions from an existing bit field without worrying about bitwise operations. Both .add()
and .remove()
can take a single permission flag or number, an array of permission flags or numbers, or multiple permission flags or numbers as multiple parameters.
const { PermissionsBitField } = require('discord.js');
const permissions = new PermissionsBitField([
PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.EmbedLinks,
PermissionsBitField.Flags.AttachFiles,
PermissionsBitField.Flags.ReadMessageHistory,
PermissionsBitField.Flags.ManageRoles,
]);
console.log(permissions.has(PermissionsBitField.Flags.KickMembers));
// output: false
permissions.add(PermissionsBitField.Flags.KickMembers);
console.log(permissions.has(PermissionsBitField.Flags.KickMembers));
// output: true
permissions.remove(PermissionsBitField.Flags.KickMembers);
console.log(permissions.has(PermissionsBitField.Flags.KickMembers));
// output: false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
You can utilize these methods to adapt permissions or overwrites without touching the other flags. To achieve this, you can get the existing permissions for a role, manipulating the bit field as described above and passing the changed bit field to role.setPermissions()
.
Resulting code
If you want to compare your code to the code we've constructed so far, you can review it over on the GitHub repository here open in new window.