Guide

Make a Discord bot in Python

From nothing to a bot that answers slash commands in your server, in about twenty minutes. You need Python installed and a Discord server you can add bots to.

10 minute read

1. Create the application

  1. Go to the Discord Developer Portal and press New Application. The name is what people will see.
  2. Open Bot. Press Reset Token and copy it. This is the bot's password: anyone with it can run your bot. Keep it somewhere private for now.
  3. On the same page, Privileged Gateway Intents can all stay off for this guide. You only need Message Content Intent if your bot will read ordinary messages, for example !commands.

2. Invite it to your server

  1. Open OAuth2, then URL Generator.
  2. Tick bot and applications.commands.
  3. Under bot permissions, tick Send Messages (and whatever else your bot will need later).
  4. Open the URL at the bottom, pick your server, and authorise.

The bot shows up in the member list, offline. It comes online when your code runs.

3. Set up the project

You need Python 3.10 or newer (python --version). Make a folder and a virtual environment, so this bot's packages stay separate from everything else:

mkdir my-bot
cd my-bot
python -m venv venv
# Windows:        venv\Scripts\activate
# macOS / Linux:  source venv/bin/activate
pip install discord.py python-dotenv
pip freeze > requirements.txt

4. Keep the token out of the code

Create a file called .env in the folder:

BOT_TOKEN=paste-your-token-here

If you use Git, add .env and venv/ to .gitignore now, before your first commit. Tokens pushed to public repositories are found by scanners within minutes.

5. Write the bot

Create bot.py:

import os
import random

import discord
from discord import app_commands
from dotenv import load_dotenv

load_dotenv()

class MyBot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.tree = app_commands.CommandTree(self)

    async def setup_hook(self):
        # Tell Discord which slash commands exist. Runs once per start.
        await self.tree.sync()

bot = MyBot()

@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")

@bot.tree.command(description="Say hello")
async def hello(interaction: discord.Interaction):
    await interaction.response.send_message(f"Hello, {interaction.user.mention}!")

@bot.tree.command(description="Roll a die")
@app_commands.describe(sides="How many sides the die has")
async def roll(interaction: discord.Interaction, sides: int = 6):
    await interaction.response.send_message(f"You rolled {random.randint(1, sides)}")

bot.run(os.environ["BOT_TOKEN"])

6. Run it

python bot.py

You should see Logged in as and your bot's name, and the bot turns green in Discord. Type / in your server: /hello and /roll are listed under your bot. The first time, it can take a minute, and reloading Discord (Ctrl+R) helps.

If it did not work

  • KeyError: 'BOT_TOKEN': the .env file is not in the folder you ran the command from, or the line is misspelt.
  • LoginFailure: Improper token has been passed: you copied the Client Secret or the Application ID instead of the token, or reset the token afterwards. Reset it and copy it again.
  • ModuleNotFoundError: No module named 'discord': the virtual environment is not active in this terminal. Activate it again.
  • Commands do not appear: the bot was invited without applications.commands. Make a new invite link with both scopes and open it again. More in slash commands in discord.py.

Where to go next

When you want it running all the time, SnowServers runs exactly this kind of bot from $3 a month, with a 7-day free trial: upload the folder (without venv and .env) or push it to GitHub, paste the token into the panel, and it stays online. Hosting a discord.py bot walks through it.