Learn to Code via Tutorials on Repl.it!

← Back to all posts
Discord.py - Rewrite Tutorial using commands extension
TheDrone7 (1777)

Discord.py Rewrite

Note : Before you start this tutorial, You should already know the language python, how to create applications in discord and you should've already invited the bot to your server.

Step 1 : Setting up

  1. Create a new folder for your bot, it can be anything. For reference, I will name it Sample Bot.

  2. Open up command prompt or terminal and type the following commands in it.

    $ pip install -U discord.py
  3. Now create a new file named main.py in your bot's folder. This file will hold the main bot.

  4. Also create a new directory named cogs in your bot's directory, this will hold our command groups (aka cogs).

    Now you're ready to code

Step 2 : The main bot

  1. Open up your main.py file. Given below is some basic code with everything explained using comments.

    from discord.ext import commands
    def get_prefix(client, message):
        prefixes = ['=', '==']    # sets the prefixes, u can keep it as an array of only 1 item if you need only one prefix
        if not message.guild:
            prefixes = ['==']   # Only allow '==' as a prefix when in DMs, this is optional
        # Allow users to @mention the bot instead of using a prefix when using a command. Also optional
        # Do `return prefixes` if u don't want to allow mentions instead of prefix.
        return commands.when_mentioned_or(*prefixes)(client, message)
    bot = commands.Bot(                         # Create a new bot
        command_prefix=get_prefix,              # Set the prefix
        description='A bot used for tutorial',  # Set a description for the bot
        owner_id=374886124126208000,            # Your unique User ID
        case_insensitive=True                   # Make the commands case insensitive
    # case_insensitive=True is used as the commands are case sensitive by default
    async def on_ready():                                       # Do this when the bot is logged in
        print(f'Logged in as {bot.user.name} - {bot.user.id}')  # Print the name and ID of the bot logged in.
    # Finally, login the bot
    bot.run('BOT TOKEN here', bot=True, reconnect=True)
  2. Now run this file, commands extension will prepare a default help command for you, you should see something similar to this when you use it: -

    Note that it doesn't show help when =help is used as this was done in DMs, and we only allowed ==help in DMs.

  3. Next we add the commands from the cogs directory.

  4. Inside the cogs directory, create a new file named basic.py.

  5. Add the following code above the

    async def on_ready():

    part in the main.py file

    cogs = ['cogs.basic']

    This basically points to the basic.py file in the cogs folder.

  6. Next, change

    async def on_ready():                                       
        print(f'Logged in as {bot.user.name} - {bot.user.id}')  


    async def on_ready():
        print(f'Logged in as {bot.user.name} - {bot.user.id}')
        for cog in cogs:

    This will load the all the files specified in the cogs variable.

  7. Next, open up the basic.py file and write the following code in it.

    from discord.ext import commands
    from datetime import datetime as d
    # New - The Cog class must extend the commands.Cog class
    class Basic(commands.Cog):
        def __init__(self, bot):
            self.bot = bot
        # Define a new command
            description='The ping command',
        async def ping_command(self, ctx):
    	    start = d.timestamp(d.now())
        	# Gets the timestamp when the command was used
        	msg = await ctx.send(content='Pinging')
        	# Sends a message to the user in the channel the message with the command was received.
        	# Notifies the user that pinging has started
        	await msg.edit(content=f'Pong!\nOne message round-trip took {( d.timestamp( d.now() ) - start ) * 1000 }ms.')
        	# Ping completed and round-trip duration show in ms
        	# Since it takes a while to send the messages
        	# it will calculate how much time it takes to edit an message.
        	# It depends usually on your internet connection speed
    def setup(bot):
        # Adds the Basic commands to the bot
        # Note: The "setup" function has to be there in every cog file

    This will add a new ping command to your bot!

  8. Save the file

Step 3 : Checking the new command

  1. Run the main.py again.

  2. Use the help command. It should look something like this: -

    Observe the new Basic category and ping command listed under it. This means that our command was successfully loaded.

  3. Now use the help ping command, should look something like this: -

    Note that in the example, it shows the prefix used by you!

  4. Finally, let's use the ping command.

    This is an example run (my internet is a bit slow)

Step 4 : Commands with arguments

  1. Open up the basic.py file and add the following command to it by typing it just below the return statement of the ping command. This is a say command that repeats the words said by the user (in bold): -

            description='The say command',
            aliases=['repeat', 'parrot'],
        async def say_command(self, ctx):
            # The 'usage' only needs to show the parameters
            # As the rest of the format is generated automatically
            # Lets see what the parameters are: -
            # The self is just a regular reference to the class
            # ctx - is the Context related to the command
            # For more reference - https://discordpy.readthedocs.io/en/rewrite/ext/commands/api.html#context
            # Next we get the message with the command in it.
            msg = ctx.message.content
            # Extracting the text sent by the user
            # ctx.invoked_with gives the alias used
            # ctx.prefix gives the prefix used while invoking the command
            prefix_used = ctx.prefix
            alias_used = ctx.invoked_with
            text = msg[len(prefix_used) + len(alias_used):]
            # Next, we check if the user actually passed some text
            if text == '':
                # User didn't specify the text
                await ctx.send(content='You need to specify the text!')            
                # User specified the text.
                await ctx.send(content=f"**{text}**")
  1. Next, let's use this command!

    As you can see, works perfectly!

Step 5 : Embeds

  1. Since, we've already done this, let's do one final command. The embed command.

    What this command will do?

    • Ask for the title of the embed.
    • Ask the user to specify the text for the embed description.
    • Set a random color to the embed.
    • Send the embed in the channel

    What this will help you learn?

    • How to create and send embeds.
    • How to wait for user's response.
  2. In your cogs directory, create a new file named embed.py

  3. In your main.py file, to the cogs list which formerly was cogs = ['cogs.basic'] add a new cog i.e. 'cogs.embed', making it - cogs = ['cogs.basic', 'cogs.embed'] . This will tell it to also treat the embed.py file as another cog.

  4. Open up the embed.py file and write the following code in it: -

  5. from discord.ext import commands
    import discord
    import random
    # These color constants are taken from discord.js library
    colors = {
      'DEFAULT': 0x000000,
      'WHITE': 0xFFFFFF,
      'AQUA': 0x1ABC9C,
      'GREEN': 0x2ECC71,
      'BLUE': 0x3498DB,
      'PURPLE': 0x9B59B6,
      'LUMINOUS_VIVID_PINK': 0xE91E63,
      'GOLD': 0xF1C40F,
      'ORANGE': 0xE67E22,
      'RED': 0xE74C3C,
      'GREY': 0x95A5A6,
      'NAVY': 0x34495E,
      'DARK_AQUA': 0x11806A,
      'DARK_GREEN': 0x1F8B4C,
      'DARK_BLUE': 0x206694,
      'DARK_PURPLE': 0x71368A,
      'DARK_VIVID_PINK': 0xAD1457,
      'DARK_GOLD': 0xC27C0E,
      'DARK_ORANGE': 0xA84300,
      'DARK_RED': 0x992D22,
      'DARK_GREY': 0x979C9F,
      'DARKER_GREY': 0x7F8C8D,
      'LIGHT_GREY': 0xBCC0C0,
      'DARK_NAVY': 0x2C3E50,
      'BLURPLE': 0x7289DA,
      'GREYPLE': 0x99AAB5,
      'DARK_BUT_NOT_BLACK': 0x2C2F33,
      'NOT_QUITE_BLACK': 0x23272A
    class Embed(commands.Cog):
        def __init__(self, bot):
            self.bot = bot
            description='The embed command',
        async def embed_command(self, ctx):
            # Define a check function that validates the message received by the bot
            def check(ms):
                # Look for the message sent in the same channel where the command was used
                # As well as by the user who used the command.
                return ms.channel == ctx.message.channel and ms.author == ctx.message.author
            # First ask the user for the title
            await ctx.send(content='What would you like the title to be?')
            # Wait for a response and get the title
            msg = await self.bot.wait_for('message', check=check)
            title = msg.content # Set the title
            # Next, ask for the content
            await ctx.send(content='What would you like the Description to be?')
            msg = await self.bot.wait_for('message', check=check)
            desc = msg.content
            # Finally make the embed and send it
            msg = await ctx.send(content='Now generating the embed...')
            color_list = [c for c in colors.values()]
            # Convert the colors into a list
            # To be able to use random.choice on it
            embed = discord.Embed(
            # Also set the thumbnail to be the bot's pfp
            # Also set the embed author to the command user
            await msg.edit(
            # Editing the message
            # We have to specify the content to be 'None' here
            # Since we don't want it to stay to 'Now generating embed...'
    def setup(bot):
        # Adds the Basic commands to the bot
        # Note: The "setup" function has to be there in every cog file
  6. Now, let's run this command!

  7. And that's it!

Step 6 : Changing the default help command

  1. As we've already seen, commands extension already comes with a built-in help command. Let's use it again!

    Pretty clearly, it's very ugly. So, let's change it!

  2. So, what we'll do is, remove the pre-defined help command and then create a new help command in the embed.py file.

  3. Open up the main.py file and make the following changes: -

    async def on_ready():
        print(f'Logged in as {bot.user.name} - {bot.user.id}')
        # Removes the help command
        # Make sure to do this before loading the cogs
        for cog in cogs:
  4. Open the embed.py file and make a new help command is given below: -

    NOTE : We're making this in the embed.py file because this help command will be using embeds too. ( though it's not necessary to do even if it uses embeds, I just don't want to create another file with all those colors).

    What this help command will do?

    • Get all the cogs registered.

    • If the user has passed a cog argument,
      then list all commands under that cog.

    • Else, list all cogs and commands under them.

         description='The help command!',
         aliases=['commands', 'command'],
      async def help_command(self, ctx, cog='all'):
         # The third parameter comes into play when
         # only one word argument has to be passed by the user
         # Prepare the embed
         color_list = [c for c in colors.values()]
         help_embed = discord.Embed(
             text=f'Requested by {ctx.message.author.name}',
         # Get a list of all cogs
         cogs = [c for c in self.bot.cogs.keys()]
         # If cog is not specified by the user, we list all cogs and commands
         if cog == 'all':
             for cog in cogs:
                 # Get a list of all commands under each cog
                 cog_commands = self.bot.get_cog(cog).get_commands()
                 commands_list = ''
                 for comm in cog_commands:
                     commands_list += f'**{comm.name}** - *{comm.description}*\n'
                 # Add the cog's details to the embed.
                     name='\u200b', value='\u200b', inline=False
                 # Also added a blank field '\u200b' is a whitespace character.
             # If the cog was specified
             lower_cogs = [c.lower() for c in cogs]
             # If the cog actually exists.
             if cog.lower() in lower_cogs:
                 # Get a list of all commands in the specified cog
                 commands_list = self.bot.get_cog(cogs[ lower_cogs.index(cog.lower()) ]).get_commands()
                 # Add details of each command to the help text
                 # Command Name
                 # Description
                 # [Aliases]
                 # Format
                 for command in commands_list:
                     help_text += f'```{command.name}```\n' \
                     # Also add aliases, if there are any
                     if len(command.aliases) > 0:
                         help_text += f'**Aliases :** `{"`, `".join(command.aliases)}`\n\n\n'
                         # Add a newline character to keep it pretty
                         # That IS the whole purpose of custom help
                         help_text += '\n'
                     # Finally the format
                     help_text += f'Format: `@{self.bot.user.name}#{self.bot.user.discriminator}' \
                         f' {command.name} {command.usage if command.usage is not None else ""}`\n\n\n\n'
                 help_embed.description = help_text
                 # Notify the user of invalid cog and finish the command
                 await ctx.send('Invalid cog specified.\nUse `help` command to list all cogs.')
         await ctx.send(embed=help_embed)
  1. This is just an example and gives you an basic idea of what it is like. Below is a sample run of this new help command.

    No cog specified, commands listed

    Embed cog specified, show the details of commands under Embed category.

    Invalid cog specified, send error message.

Quick Tip

Use .env files to store your token. I've used .env file to store my token in the final code. You can learn more about how to use .env files here.

The End

Those are all the basics you'll need to know, refer to API Reference for reference on the discord.py commands extension. You can also refer to the main library docs. All of the above code can be obtained at here. Also you can visit this link to learn how to host your discord bots on repl.it!

BolehNgopi (1)

Uhh I still confused 😅

How to make cogs?

NichoJ23 (1)

Everything works for me except for when I try to use the help command on a specific class (i.e. "=help basic"), and then it brings up this message:

the same thing happens when I try to fork your finished code

TheDrone7 (1777)

@NichoJ23 looks like discord.py rewrite has had a new big update. I'll update this ASAP. Thanks for letting me know!

TheDrone7 (1777)

@NichoJ23 I'm sorry it took so long but it's been fixed now! Thanks again for letting me know about this.

NichoJ23 (1)

Thanks for your help! @TheDrone7

raoulthegeek (1)

maybe you could update the libs, as Discord.py Rewrite has been officially released, and replaced the async version

julesjulicher2 (0)

it doesn't recognise the ping cmd, when i exactly copy the code and run it's just like the cog doesn't exist

McJoe21 (1)

Indent error on line 20 (cogs.basic)

sunnylubby (0)

Hello is anyone still available here? I'm getting this error for the custom help:

2020-05-03T14:05:40.834559+00:00 app[worker.1]: discord.ext.commands.errors.CommandInvokeError: Command raised an exception: HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
2020-05-03T14:05:40.834560+00:00 app[worker.1]: In embed.fields.10.name: This field is required
2020-05-03T14:05:40.834561+00:00 app[worker.1]: In embed.fields.10.value: This field is required
2020-05-03T14:05:40.834561+00:00 app[worker.1]: In embed.fields.12.name: This field is required
2020-05-03T14:05:40.834561+00:00 app[worker.1]: In embed.fields.12.value: This field is required
2020-05-03T14:05:40.834562+00:00 app[worker.1]: In embed.fields.4.name: This field is required
2020-05-03T14:05:40.834562+00:00 app[worker.1]: In embed.fields.4.value: This field is required
2020-05-03T14:05:40.834563+00:00 app[worker.1]: In embed.fields.6.name: This field is required
2020-05-03T14:05:40.834563+00:00 app[worker.1]: In embed.fields.6.value: This field is required
2020-05-03T14:05:40.834563+00:00 app[worker.1]: In embed.fields.8.name: This field is required
2020-05-03T14:05:40.834564+00:00 app[worker.1]: In embed.fields.8.value: This field is required

Thank you!

ripu (3)

Nice job good tutorial

colezx5014 (0)

I'm very confused. Whenever I try to install discord.py using the "pip" thing, it does not work and it says "'pip' is not recognized as an internal or external command,
operable program or batch file". What do I do?

TheDrone7 (1777)

@colezx5014 while installing python on your device, it gives you an option to add the directory to the path environment variable. You might have not checked it. You can either add the directory to the environment variable manually or reinstall python and make sure the option is checked.


@TheDrone7 What are cogs and do I have to use terminal or command prompt for this? Can you please share the link of your code so I can fork it and just do it?

P.s I am new to coding

TheDrone7 (1777)

@haniel2007 I really have no clue what cogs means but I simply assume it's short for command groups cuz that basically defines them perfectly. As for the next part, command prompt or powers hell is what you'd use on windows, on Linux, you'd use terminal, on repl.it you'd use the repl package manager or just the requirements.txt solution. It's just different names for the same thing depending on the operating system. Nextly, the final code is linked at the end of the tutorial.
Lastly, I recommend you to learn about coding and python first and then return to this tutorial. This one is not for beginners or people who are not well versed with the language since I only explain what is discord.py doing and not what python is doing. Hope that'll be all.

AkimotoRyou (1)

It gives me this error
TypeError: cogs must derive from Cog

AkimotoRyou (1)

@TheDrone7 I've tried to fork it to my repl.it to get exact code, but it throws this error :

Ignoring exception in on_ready
Traceback (most recent call last):
  File "/home/runner/.site-packages/discord/client.py", line 227, in _run_event
    await coro(*args, **kwargs)
  File "main.py", line 36, in on_ready
  File "/home/runner/.site-packages/discord/ext/commands/bot.py", line 618, in load_extension
  File "/home/runner/cogs/basic.py", line 74, in setup
  File "/home/runner/.site-packages/discord/ext/commands/bot.py", line 543, in add_cog
    raise TypeError('cogs must derive from Cog')
TypeError: cogs must derive from Cog
TheDrone7 (1777)

@AkimotoRyou I saw it and observed that there have been a few changes in how discord.py-rewrite works. Please be patient while I update my tutorial and example.

TheDrone7 (1777)

@AkimotoRyou Thanks for the patience, I have updated both my tutorial and the final example to match the latest requirements. Thanks for informing me.

pooopycaca68 (1)

@TheDrone7 the help command is not working it does not respond and it says not found somebody please help me i need it

anasir514 (0)

@pooopycaca68 did u call the help by using your specified prefix?

hillsam001 (25)

When I use the ping command, it says I need to use context, and same as with help ping...

TheDrone7 (1777)

@hillsam001 it might be due to you using discord.py async. Although I'm not sure, I think I'll need more info like link to a repl and what error exactly you're getting.