Learn to Code via Tutorials on Repl.it!

← Back to all posts
Top tips for your discord.py Discord bots!
CoolJames1610 (765)

Top tips for your discord.py Discord bots!


Well hello everyone and I am creating this little handy tutorial for your python discord bots.
I am sure you can replicate this in any other language, so if you want, please re-create this (crediting me) and comment the post and I'll add it to this post! :)

If you enjoyed this little tutorial then please comment and if you have any other cool little tips to make your discord bots better then please also comment! :D

Without any further ado, let's begin!

Note that this is written in the rewrite version of discord.py not the async branch.


1) Customisable prefix (by server)

Reference
Okay so we all know that bots have a prefix.

E.g. ! or > or $ or - or = - We call these common prefixes.

But if I'm in a server with lots of other bots that use these common prefixes then when I type a command in, several bots may reply to it at once! Example: a help command.

As the bot developer, I can make people's lives easier by allowing them to set the prefix of the bot.
We can achieve this by using json.

1) First we need to import discord & json obviously and some additional commands.

import discord
from discord.ext import commands
from discord.ext.commands import Bot
from discord.ext.commands import has_permissions, MissingPermissions, is_owner
import json

2) Then we can create a file called prefixes.json.

This is where we will store the prefixes of each server.

3) Creating the get-prefix command.

We will create the command with two arguments: client and message.

def get-prefix(client, message)

Now we want to load the data from our json file and we can do this by loading the data.

def get-prefix(client, message):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)

This will load all the data from the text file. But now we want to get the server's prefix of the one that we are in. So, we will get the entry that contains the id of that server.

def get-prefix(client, message):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)
  try:
    x = prefixes.get(str(message.guild.id), "!")
    bot.prefix = x
    return x
  except:
    pass

As you can see, the starting prefix is !. You can change this to whatever you want.
We have a try and except clause to catch any errors - but there shouldn't be any :)
So now we know how to retrieve the data, how do we put the data in the file in the first place?

4) Adding and removing prefixes when our bot joins or leaves a server

Well, we set all of this when the bot joins the server and if the bot were to leave the server then we can remove the data, so our file only contains relevant data.

So let's get the command where we get notified whenever our bots joins a server.

@bot.event
async def on_guild_join(guild):

Now we will re-use the code that loads the data from the file.

@bot.event
async def on_guild_join(guild):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)

Now we have loaded the data, we want to add to the data.
And we do this by setting the prefix to the "normal" prefix of our bot.

@bot.event
async def on_guild_join(guild):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)
  prefixes[str(guild.id)] = "!"

By using guild.id as the key, each key and value in the file will be unique.
Next we need to dump the data into the file. And we do this as shown below:

@bot.event
async def on_guild_join(guild):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)
  prefixes[str(guild.id)] = "!"
  with open("prefixes.json", "w") as f:
    json.dump(prefixes, f, indent=4)

So now we have successfully added a server into the file!
To remove a server's information, all we do is replace:
async def on_guild_join(guild): with async def on_guild_remove(guild):.
prefixes[str(guild.id)] = "!" with prefixes.pop(str(guild.id)).

So now we can put in information when a bot joins a server and remove information when it leaves a server.
But how do we allow the user to change the prefix?

5) Allowing the user to change the prefix

Now we are going to use bot.command() as this is a command that the user can use.

@bot.command(name = "set-prefix", aliases=["sp"])

name is what the command is called, and aliases is other names that can all up this command. If the prefix were to be !, !set-prefix and !sp would do the same thing.

Now, we don't want anybody to be able to change the prefix of the server, otherwise that would just be ANNOYING!
We can use the @has_permissions() decorator to make sure that only "verified" people are able to use the command.
For this tutorial anyone with a role that allows them to manage the server will be able to use this command.

@bot.command(name = "set-prefix", aliases=["sp"])
@has_permissions(manage_guild=True)

You can also put administrator=True if you want the control to be even more restrictive.
Next we have to define our function:

@bot.command(name = "set-prefix", aliases=["sp"])
@has_permissions(manage_guild=True)
async def _setprefix(ctx, prefix):

I have put a leading underscore so it cannot be used at all. You can name it anything you want it to be.
We have two arguments: ctx and prefix.
ctx is the context of the message and Discord gives us valuable information about the message.
prefix is what the user wants to change the prefix to. E.g. "$".
As we have seen before, we need to load the data:

@bot.command(name = "set-prefix", aliases=["sp"])
@has_permissions(manage_guild=True)
async def _setprefix(ctx, prefix):
with open("prefixes.json", "r") as f:
  prefixes = json.load(f)

Then from the file, we locate the prefix that belongs to the server. And re-dump the data.

@bot.command(name = "set-prefix", aliases=["sp"])
@has_permissions(manage_guild=True)
async def _setprefix(ctx, prefix):
with open("prefixes.json", "r") as f:
  prefixes = json.load(f)
prefixes[str(ctx.guild.id)] = prefix
with open("prefixes.json", "w") as f:
  json.dump(prefixes, f, indent=4)

Then we can send a message to the user and then create a variable using bot - which means it can be used anywhere in the program.

@bot.command(name = "set-prefix", aliases=["sp"])
@has_permissions(manage_guild=True)
async def _setprefix(ctx, prefix):
  with open("prefixes.json", "r") as f:
    prefixes = json.load(f)
  prefixes[str(ctx.guild.id)] = prefix
  with open("prefixes.json", "w") as f:
    json.dump(prefixes, f, indent=4)
  await ctx.send(f"Server prefix changed to: `{prefix}`")
  bot.prefix = prefix

And that's it!
You have successfully created a command that lets the user change the prefix of the bot for their server!

More to think about:

  • You can use the bot.prefix in your help commands so that if the user changes the prefix, the change is reflected everywhere.
  • You could edit your bot's username in the server so that people know what the prefix is:
    Repl [!] --> Repl [$]

2) Uptime of your bot

Reference
So we all want to know how long our bot has stayed up but how exactly do we do this?
As we learnt in the previous chapter, we can use bot. to create a variable that won't change until you restart the program.
That means we can use this to determine the start of the program and then use datetime to get the time NOW which would give us the uptime of the bot!

So, let's import what we need:

import discord
from discord.ext import commands
from discord.ext.commands import Bot
from datetime import datetime

Now we create another command: uptime

@bot.command()
async def uptime(ctx):

As you can see here, we haven't added a parameter for name. So the program will automatically use the name we have used to define the function as the command name as we haven't added a leading underscore.
We takeaway the time the bot started away from the time now and use divmod to get different times.

@bot.command()
async def uptime():
  delta_uptime = datetime.utcnow() - bot.launch_time
  hours, remainder = divmod(int(delta_uptime.total_seconds()), 3600)
  minutes, seconds = divmod(remainder, 60)
  days, hours = divmod(hours, 24)
  return (f"{days}d, {hours}h, {minutes}m, {seconds}s")

Obviously, you can change this to only show hours, or only minutes - how ever you want.


3) Bot's status

Reference
Statuses are cool. You can put anything you want for anybody to see.
But how do we set statuses for our bots? Can we simply put anything?
No, unfortunately, but we can use the change_presence to set the status of the bot to any of the options below:

  • Game
  • Streaming
  • Listening
  • Watching

Gaming

For the gaming status (one of the most used), we set the activity to discord.Game:

await bot.change_presence(activity=discord.Game(name="a game"))

You can then set name to whatever you want your bot to be "playing"

Streaming

For the streaming status (Dank Memer uses this I believe), we set the activity to discord.Streaming:

await bot.change_presence(activity=discord.Streaming(name="My Stream", url=my_twitch_url))

By "streaming", we mean streaming on Twitch and so if you would like, you can add your Twitch url so people can click on your bot and go directly to your streaming page.

Listening

For the listening status, we set the activity to discord.Activity and define type to discord.ActivityType.listening:

await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="a song"))

You can then change name to whatever you want

Watching

For the listening status, we set the activity to discord.Activity and define type to discord.ActivityType.watching:

await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie"))

You can then change name to whatever you want

You can then put your bot.change_presence inside your:

@bot.event
async def on_ready():
  await bot.change_presence(activity=discord.Game(name="a game"))

You can also have status=discord.Status.idle in your change_presence if you wish to change your bots status from online to any of the below options:

  • idle
  • dnd
  • invisible

More to think about:

  • You can create a loop to change your bot's status every so often. Discord has a limit for this, so make sure you read up on it.

4) Owner-only commands

Reference
There are commands that we, as the bot developer, that we want to execute to test some things. But we don't want anybody to be able to access these commands as the commands can change things about the bot.
Now, we can simply add an if statement to check whether the user is the owner or not:

@bot.command(name = "eval")
async def _eval(ctx):
  if ctx.message.author.id != 685842582856532059:
    return
  else:
    # blah blah blah

But this is annoying. First, you have to remember to code this bit and also to get your id every single time.
Also, this slows the program down. But fortunately, there is a QUICKER way!
The decorator: @is_owner() makes it easier and quicker to ensure that only the owner of the bot can execute the command.

Let's see what we have to import:

import discord
from discord.ext import commands
from discord.ext.commands import Bot
from 
discord.ext.commands import has_permissions, MissingPermissions, is_owner

Now we can use the @is_owner() decorator under the @bot.command() and it will ensure that only we can use the command:

@bot.command(name = "eval")
@is_owner()
async def _eval(ctx):
  # blah blah blah

See? Much better and easier too :D
Since it is not allowed for anybody to check which guilds your bot is in, you can use this @is_owner() decorator to restrict it to only YOU

5) Sending images saved locally in Repl

Reference
This took me sooooo long to figure out but when I did, I was so GLAD!
So, say you have a image in your repl - repl.png and you want to add it in your embed as either the image or the thumbnail. This can be useful if you just want to display your bot's profile picture on the embed.
Now, you could upload it to an external website such as imgur but sometimes, Discord isn't able load the images.

So what we can do is use discord.File to create a file so that we are able to send an image along with the embed.
The first parameter takes the location of the image - Images/repl.png.
The second parameter takes the name of the image - filename=repl.png

file = discord.File("Image/repl.png", filename="repl.png")

Then, for either set_image or set_thumbnail, we need to set a url and we can do this by doing:
url = "attachment://repl.png"
Discord will allow this as your url

file = discord.File("Image/repl.png", filename="repl.png")
embed.set_thumbnail(url="attachment://repl.png")

However, when we send the embed using embed=embed we also have to add file=file so that Discord adds our image into the embed.

file = discord.File("Image/repl.png", filename="repl.png")
embed.set_thumbnail(url="attachment://repl.png")
await embed.send(file=file, embed=embed)

Perfect!
Now we can set images or thumbnails to our locally saved images!


Making Discord bots is so fun and the more you code, the better you will become. The more little "secrets" you will learn to make your life easier as a programmer!


Thank you for reading this tutorial!

Took me a while lol

If there is anything that needs correcting, then please comment :D
I have included references to all the stackoverflow websites that I eventually found answers to.
So technically this tutorial took months to make xD

If you have any questions about how to code using discord.py, then don't hesitate to ask :D

I hope you enjoyed!


Links to other programming languages:

Discord.py: https://repl.it/talk/learn/Top-tips-for-your-discordpy-Discord-bots/81740


@CoolJames1610

Comments
hotnewtop
PyCoder01 (56)

This is not the best discord.py bot basics tutorial because there are more basics of discord bots and there are more basic ways to make a discord bot than this.

CoolJames1610 (765)

@PyCoder01 never said it was a discord.py bot basicd tutorial did I?