Discord Intermediate Roles
Tags: discord, python, roles, bot
Published: 2022-06-20 03:04:52

## Creating Server Specific Role Commands

Setting up a bot to give a promotion or demotion could come in handy when trying to track when users were updated on their server status (for those niche Discords out there).

First things that will be needed are the roles in question to be promotable. The IDs of the roles will be set as variables or in a dictionary which is how this will be shown.

Next will be the server ID, this will be used in the Cog file in order to keep the command separate from other servers that the bot may be in to keep things tidy for when you run the `help` command.

Above the class declaration, set your dictionary ordered 1 through however many roles will be used during the command check. Roles should be ordered from lowest "rank" to highest. 

`role1ID` should be the lowest and `role3ID` should be the lowest

```python
import nextcord
from nextcord.ext.commands.core import has_permissions
from nextcord.utils import get
from nextcord.ext import commands

RANKS = {
  1: role1ID,
  2: role2ID,
  3: role3ID
}

class ServerRoles(commands.Cog):
  def __init__(self, client):P
    self.client = client

  def cog_check(self, ctx):
    return ctx.guild.id == <the-server-id>
...

def setup(client):
  client.add_cog(ServerRoles(client))
```

The file is setup for use for the specified server now to add in the logic for the promotion command.

```python
@commands.command(brief="Gives promotion")
@has_permissions(manage_roles = True)
async def promote(self, ctx, user: nextcord.Member):
  def getRole(key):
    return get(ctx.guild.roles, id = RANKS.get(key))
  
  # Loop through the RANKS dictionary for the current role
  for key, value in RANKS.items():
      try:
        currentRole = getRole(key)
        if key != 1:
          previousRole = getRole(key -1)
        if key != 3:
          nextRole = getRole(key + 1)
        if key == 3:
          await ctx.send(f'{user} already has the highest attainable role!')
          return
        if currentRole in user.roles:
          previousRole = currentRole
          print(f'current role in user.roles section {currentRole}')
          print(f'{user} has the {currentRole} role')
          print(f'{user} will recieve the {nextRole} role and lose the {previousRole} role')
          await user.add_roles(nextRole)
          await user.remove_roles(previousRole)
          await ctx.send(f'{user} has been promoted to {nextRole}')
          return

      except:
        print("PROMOTE FAILED SOMEWHERE!!\n")
        return
      finally:
        print("EOL for promote command")
```

The demotion command will be very similar to the promotion command but checks for the lowest rank on the list as to not have a runtime error and cause the bot the crash out.

```python
@commands.command(brief="Gives demotion")
  @has_permissions(manage_roles=True)
  async def demote(self, ctx, user: nextcord.Member):
    def getRole(key):
      return get(ctx.guild.roles, id = RANKS.get(key))
    for key, value in RANKS.items():
      try:
        currentRole = getRole(key)
        print(currentRole)
        if currentRole in user.roles and key == 1:
          print(f'{currentRole} is the lowest role')
          await ctx.send(f'{user} is currently at the lowest rank!')
          return
        if key != 1:
          previousRole = getRole(key - 1)
        if currentRole in user.roles:
          # Give previousRole
          print(f'{user} has {currentRole} to be removed and {previousRole} to be given')
          await user.add_roles(previousRole)
          await user.remove_roles(currentRole)
          await ctx.send(f'{user} has been demoted to {previousRole}')
          return
        else:
          print('something else needs to happen before here')
      except:
        print('DEMOTION FAILED\n')
        return
      finally:
        print('EOL for demotion\n')
```

## Servers with minimum role requirement

On Discords that require a "required" low role in order to be able to talk/connect/etc the order of the RANKS dictionary will need to be flipped to a tiered system to show that `1: role1ID` would be the highest tier of the Discord and will work towards the lowest rank, of which should be the required role.

```python
# without required role
if key != 1:
  previousRole = getRole(key - 1)
if key != 3:
  nextRole = getRole(key + 1)

# with required role
if key != 3:
  previousRole = getRole(key + 1)
if key != 1:
  nextRole = getRole(key - 1)
```

This will flip the check so it looks at the highest rank first rather than the lowest rank.

The other change will need to be made in order to keep to lowest rank when promoting and user up and demoting them back to the lowest rank:

```python
# promote command

# Check for top rank
if currentRole in user.roles and key == 1:
  await ctx.send("This is the highest rank!")
  return

# Check for bottom rank
if currentRole in user.roles and key == 3:
  await user.add_roles(nextRole)
  return

# demote command

# Check for bottom role
if currentRole in user.roles and key == 3:
  await ctx.send("User is at the lowest rank already!")
  return

# Remove currentRole but retain bottom role
if currentRole in user.roles and key == 2:
  await user.remove_roles(currentRole)
  return
```