This is the fourth and last part of the Discord.JS V14 tutorial that I've published. You can read the third part here. You can also read the full version of this tutorial on Medium if you prefer.
Creating Roles, like Threads, does not care about the Channel or its Categories. Roles also have many interesting properties that we can modify, like color. Let's tweak a little the code we used for creating a Channel.
// Importing SlashCommandBuilder is required for every slash command
// We import PermissionFlagsBits so we can restrict this command usage
const { SlashCommandBuilder,PermissionFlagsBits } =require("discord.js");
module.exports = {
data:newSlashCommandBuilder()
.setName("createrole") // Command name matching file name.setDescription("Creates a new role")
// Role name.addStringOption((option) =>
option
.setName("rolename") // option names need to always be lowercase and have no spaces.setDescription("Choose the name to give to the role")
.setMinLength(1) // A role needs to be named.setMaxLength(100) // Hard limit set by Discord for role names.setRequired(true)
)
// You will usually only want users that can create new Channels to// be able to use this command and this is what this line does.// Feel free to remove it if you want to allow any users to// create new Channels.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels)
// It's impossible to create roles inside DMs, so// it's in your best interest in disabling this command through DMs.setDMPermission(false),asyncexecute(interaction) {
// Before executing any other code, we need to acknowledge the interaction.// Discord only gives us 3 seconds to acknowledge an interaction before// the interaction gets voided and can't be used anymore.await interaction.reply({
content:"Fetched all input and working on your request!",
});
// After acknowledging the interaction, we retrieve the string sent by the userconst chosenRoleName = interaction.options.getString("rolename");
// Do note that the string passed to the method .getString() needs to// match EXACTLY the name of the option provided (line 12 in this file).// If it's not a perfect match, this will always return nulltry {
// Create the roleawait interaction.guild.roles.create({
name: chosenRoleName,
});
// Inform the user about the role creation being successfulawait interaction.editReply({
content:"Your role was created successfully!",
});
return;
} catch (error) {
// If an error occurred and we were not able to create the Channel// the bot is most likely received the "Missing Permissions" error.// Log the error to the console
console.log(error);
// Also inform the user that an error occurred and give them feedback// about how to avoid this error if they want to try againawait interaction.editReply({
content:"Your role could not be created! Please check if the bot has the necessary permissions!",
});
return;
}
},
};
To create a Role, you need to access the roles of a server and then create a new Role there. Creating a Role can take some additional configuration, but all settings are optional (a Role with no configuration will be called "new role" and have no color). As in previous lessons, we create a Role that has a name provided by the user and, after that Role is created, we edit the initial message with a success message (line 46).
Now that we know how to create a role, how about we learn how to create a colored role?
Creating a colored role
Discord Roles can have any color you want. Discord even provides us with some colors they have standardized. We will both offer to use Discord's standard color selection, while also allowing users to customize exactly the color they want for the role, using hex codes for RGB colors.
// ...
// initial code unchanged
// ...// Role color options using Discord's defaults
.addStringOption((option) =>
option
.setName('rolecolor')
.setDescription('Select a color for your role (using Discord defaults)')
.addChoices(
{ name:'Aqua', value:'0x1abc9c' },
{ name:'Green', value:'0x57f287' },
{ name:'Blue', value:'0x3498db' },
{ name:'Yellow', value:'0xfee75c' },
{ name:'LuminousVividPink', value:'0xe91e63' },
{ name:'Fuchsia', value:'0xeb459e' },
{ name:'Gold', value:'0xf1c40f' },
{ name:'Orange', value:'0xe67e22' },
{ name:'Red', value:'0xed4245' },
{ name:'Grey', value:'0x95a5a6' },
{ name:'Navy', value:'0x34495e' },
{ name:'DarkAqua', value:'0x11806a' },
{ name:'DarkGreen', value:'0x1f8b4c' },
{ name:'DarkBlue', value:'0x206694' },
{ name:'DarkPurple', value:'0x71368a' },
{ name:'DarkVividPink', value:'0xad1457' },
{ name:'DarkGold', value:'0xc27c0e' },
{ name:'DarkOrange', value:'0xa84300' },
{ name:'DarkRed', value:'0x992d22' },
{ name:'DarkerGrey', value:'0x7f8c8d' },
{ name:'LightGrey', value:'0xbcc0c0' },
{ name:'DarkNavy', value:'0x2c3e50' },
{ name:'Blurple', value:'0x5865f2' },
{ name:'Greyple', value:'0x99aab5' },
{ name:'DarkButNotBlack', value:'0x2c2f33' }
)
)
// Role color options using a hex code or integer
// relevant link for hex codes: https://www.rapidtables.com/web/color/RGB_Color.html
.addStringOption((option) =>
option
.setName('customrolecolor')
.setDescription(
'Select a custom color for your role (hex code only. overrides "rolecolor")'
)
.setMinLength(8)
.setMaxLength(8)
)
// ...
// code in between unchanged
// ...const chosenRoleName = interaction.options.getString('rolename');
const chosenRoleColor =
interaction.options.getString('customrolecolor') ??
interaction.options.getString('rolecolor') ??undefined;
// ...
// code in between unchanged
// ...
name: chosenRoleName,
color: chosenRoleColor,// ...
// rest of the code unchanged
// ...
The first of the options we added is rolecolor. This option has 25 choices with colors that were standardized by Discord on their role color selection and their brand color palette. This is the easiest way for users to pick a color without needing to find a specific hex code.
The second option we added is customrolecolor. This allows users to create a Role with ANY color they want, by using a hex code. There are various websites that can be used to get a hex code from a color. The one I've been using for years is RapidTables.
.setMinLength(8) .setMaxLength(8) (lines 58 and 59)
You might have noticed that we have those strict limiters in place. This is because every RGB hex code has exactly 8 digits, the first two being 0x which identifies that the following digits are a hexadecimal number. The next two digits are responsible for the Red, the 5th and 6th digits are responsible for the Green and the last 2 digits are responsible for the Blue.
If you noticed that we mention that customrolecolor overrides rolecolor on line 56, this is the reason why. Since a single Role can't have two colors, we need to prioritize one of the inputs over the other and since it takes more effort to find a custom RGB hex code, we will assume that the user would rather use that color instead, in case they picked a rolecolor by mistake (or intentionally, since some users enjoy trying to break things just because they can). Here we use the Javascript Nullish Coalescence operator ?? to check if the user provided any custom value. If they didn't, then we check if they picked one of Discord's standard colors. If they also didn't, then we set the color as undefined, so a colorless Role can be created as the default fallback.
Finally, we use either the customrolecolor or the rolecolor or undefined to set the color (or no color) for the Role we will be creating.
Now we have a Role and it can have a color, but what's the use of a Role that no one possesses? Let's grant this Role to some Members.
Creating a role and then granting it to members
Granting a Role to a Member is very simple, you just need to access their Roles and then add the Role. Let's take a look at what that code would look like.
// ...
// initial code unchanged
// ...// Member that should get the role
a.addMemberOption((option) =>
option
.setName("membertoreceiverole")
.setDescription("The user you want to give the newly created role to")
.setRequired(true)
);
// Grant role to the member using the command
a.addBooleanOption((option) =>
option
.setName("grantroletocommanduser") // option names need to always be lowercase and have no spaces.setDescription("Choose you should be granted the role after creation")
);
// ...
// code in between unchanged
// ...const chosenRoleColor = interaction.options.getString("customrolecolor") ??
interaction.options.getString("rolecolor") ??undefined;
const memberNeedingRole = interaction.options.getMember("membertoreceiverole");
const grantRoleToSelf =
interaction.options.getBoolean("grantroletocommanduser") ??false;
// ...
// code in between unchanged
// ...// Check if the user selected "True" to the option to grant role to self
if (grantRoleToSelf ==true) {
// If they did, navigate their properties until their roles and// add the newly created role to themawait interaction.member.roles.add(coloredRole).catch((e) => {
// If it fails, send a followUp message with the error
interaction.followUp({
content:"Failed to give you the new role. Do you have any roles with higher priority than me?",
ephemeral:true,
});
});
}
// Check if a guild member was provided to receive the role
if (memberNeedingRole !=null) {
// Check if the command user requested to get the role// and also provided their own member to receive the roleif (
interaction.member.id === memberNeedingRole.id && grantRoleToSelf ==true
) {
// If they did, give them an ephemeral error message
interaction.followUp({
content:"You were already granted the role!",
ephemeral:true,
});
}
// Check if the command user provided a different member// than themselves to receive the roleif (
interaction.member.id !== memberNeedingRole.id || grantRoleToSelf ==false
) {
// If they did, navigate their properties until their roles and// add the newly created role to themawait memberNeedingRole.roles.add(coloredRole).catch((e) => {
// If it fails, send a followUp message with the error
interaction.followUp({
content:"Failed to give the new role to the member. Do they have any roles with higher priority than me?",
});
});
}
}
// ...
// rest of the code unchanged
// ...
This option allows the user to select a Member that will receive the role, once it has been created. This will not ping them or notify them in any way.
This option was added to allow the person using the command to get the Role added to them after being created if they select true when using the command.
Fetches the user input from the options mentioned above. Note how grantRoleToSelf will default to false if the user doesn't select an option. This means that the only way for the user to be granted the Role they have created is by manually selecting true when using the command.
Checks if the user requested to give themselves the Role after creation and, if they did, attempt to give it to them. This, just like giving a Role to any Member, is prone to fail if the bot doesn't have permission to Manage Members or if the Member that we are trying to give the Role already has another Role that has higher priority than the all of bot's Roles.
Check if the Member provided is the same person as the user that triggered the command and if they have previously asked to receive the Role. This will only give them an error message if they asked for the Role twice, otherwise, it will just give them the role.
If the Member provided is different than the user triggering the command or if a Member was provided and the user didn't request to have the Role added to themselves with the other option, grant the Member to have the Role.
That was quite a bit of code we added and with that, we have also covered an edge case where users can try to give themselves the same Role twice. Roles are very complex entities and there is a lot more that can be done with them, like setting up additional permissions and updating them post-creation, but that's a lesson for another day.