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 theasync
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 thesecommon 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
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.
@PyCoder01 never said it was a discord.py bot basicd tutorial did I?