Skip to content
Advertisement

Bot unable to assign roles despite having the correct privileges

While developing a custom verification bot for one of my Discord servers I encountered a seemingly unfixable permission error. I am using the pycord rewrite fork (Pycord v2.3) and the exception I am encountering is:

discord.errors.Forbidden: 403 Forbidden (error code: 50013): Missing Permissions

The issue is that even though I have invited my bot to my Discord server with administrative permissions it cannot edit/assign roles for applicable server members (anyone who’s not the owner or admin) and instead throws the above exception. Despite this seemingly arbitrary permission issue, my bot can perform other tasks that an admin is able to carry out such as editing server member nicknames. To add the oddity of the issue, when I view the server as the bot role I can edit member roles even though the bot by itself cannot. I have exhausted all other resources and have failed to find an answer to this issue so I am hoping someone here with in-depth knowledge of the pycord rewrite module could help me out.

My bot’s source code can be found below:

from pathlib import Path
import hashlib
import discord
from discord.ext import commands


class EmployeeDatabase:
    def __init__(self):
        self.filename = "employee_database.dsv"
        path = Path(self.filename)
        if not path.is_file():
            open(self.filename, "x").close()

    def hash(self, value) -> str:
        return hashlib.md5(value.encode("utf-8")).hexdigest()

    def is_duplicate(self, code: str) -> bool:
        with open(self.filename, "r") as database:
            if self.hash(value=code) in [i.rstrip() for i in database.readlines()]:
                return True
            else:
                return False

    def read_all(self) -> list[str]:
        with open(self.filename, "r") as database:
            employee_code_hashes = database.readlines()
        return employee_code_hashes

    def insert(self, code) -> None:
        new_hash = self.hash(value=code)
        print(new_hash)
        with open(self.filename, "r+") as database:
            hashes = [i.rstrip() for i in database.readlines()]
            print(hashes)
            if new_hash in hashes:
                raise Exception("Attempted to insert a duplicate hash")
            else:
                if not hashes:
                    database.write(str(new_hash))
                else:
                    database.write(f"n{new_hash}")

    def verify_employee(self, code: str) -> int:
        if self.is_duplicate(code=code):
            return 1
        elif len(code) != 6:
            return 0
        elif not code.isdigit():
            return 0
        else:
            self.insert(code=code)
            return 2


class MyModal(discord.ui.Modal):
    def __init__(self) -> None:
        super().__init__(
            discord.ui.InputText(
                label="Name",
                placeholder="First and last name",
                custom_id="name_input"
            ),
            discord.ui.InputText(
                label="Employee Code",
                placeholder="Code",
                custom_id="code_input"
            ),
            title="Employee Verification"
        )

    async def callback(self, interaction: discord.Interaction):
        child_values = {child.custom_id: child.value for child in self.children}
        name_input = child_values["name_input"].split(" ")
        full_name = f"{name_input[0].capitalize()} {name_input[1].capitalize()[0]}."
        employee_code = child_values["code_input"]
        
        verified_role = discord.utils.get(interaction.guild.roles, name="Verified")

        await interaction.user.edit(nick=full_name, roles=[verified_role])
        await interaction.response.send_message("Verified")



BOT_TOKEN = "bot token here"
intents = discord.Intents.all()
client = commands.Bot(intents=intents)
server_ids = [server ids here]


@client.event
async def on_ready() -> None:
    print("Bot initialized")


@client.event
async def on_member_join(member) -> None:
    pass


@client.slash_command(guild_ids=server_ids, name="ping", description="Pings bot")
async def ping(context):
    await context.respond(f"{round(client.latency * 1000)}ms ping")


@client.slash_command(guild_ids=server_ids, name="verify")
async def verify(context):
    modal = MyModal()
    await context.send_modal(modal=modal)


if __name__ == "__main__":
    client.run(BOT_TOKEN)

Below is a list of my attempted fixes:

  1. I have made sure that my bot is assigning valid server roles by testing the role I am trying to assign against a list of all assignable roles provided by the server.

  2. I have tried using different methods to attempt to edit member roles such as member.add_roles() and made sure the problem is purely permission based and not an issue with the pycord module.

Advertisement

Answer

In the server settings, in the role hierarchy, the bot needs to have a role above the other roles it’s trying to assign. For example:

  • Role A
  • Role B
  • Role C
  • Role D
  • etc

If my bot has Role C, it can only assign Role D and below to other users. If I want my bot to be able to assign Role A and below, I need to have a role that’s higher than it in my role hierarchy. This is true regardless of settings/permissions. It’s designed that way so you can have multiple tiers of admins/etc so those lower down can’t remove admin/other perms from those higher on the ladder. The only exception for this is the server owner – they can do whatever they want regardless of role/permissions.

You can read more about role management from the Discord here.

I would wager that your bot does not have a role high enough for it to be able to assign the roles you want it to.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement