How to make a Discord bot in Python (discord.py)

Make a Discord bot in Python from scratch with discord.py: create the app, write slash commands, add a welcome message, test it on your computer and host it for free on Torchlite.

4 min readBy Torchlite teamUpdated

How to make a Discord bot in Python (discord.py)
On this page

This tutorial builds a small but real Discord bot in Python with discord.py: two slash commands, /hello and /add, and a message that welcomes new members. You test it on your own computer, then put it on a free Torchlite Bot server so it stays online without your computer.

You need Python 3.12 or newer and a Discord account. No earlier bot experience is needed.

Create the bot in Discord

  1. Open the Discord Developer Portal and click New Application. The name you give it is the bot's name.
  2. Open Bot in the left menu and click Reset Token. Copy the token and keep it somewhere private.
  3. Further down on the same page, switch on Server Members Intent. The welcome message needs it; the slash commands do not.
  4. Open OAuth2, then URL Generator. Tick bot and applications.commands, and under Bot Permissions tick Send Messages.
  5. Open the generated URL, pick your Discord server and click Authorize.

Set up the project

Make a folder for the bot with two files. The first is requirements.txt, the list of packages:

text
discord.py

Install it once on your computer:

bash
pip install -r requirements.txt

Write the bot

The second file is bot.py:

python
import os

import discord
from discord import app_commands


class Bot(discord.Client):
    def __init__(self):
        intents = discord.Intents.default()
        intents.members = True  # for on_member_join; switched on in the portal
        super().__init__(intents=intents)
        self.tree = app_commands.CommandTree(self)

    async def setup_hook(self):
        # Sends the slash commands to Discord once, at startup.
        await self.tree.sync()


bot = Bot()


@bot.event
async def on_ready():
    print(f"Online as {bot.user} in {len(bot.guilds)} server(s).")


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


@bot.tree.command(name="add", description="Add two numbers")
@app_commands.describe(a="The first number", b="The second number")
async def add(interaction: discord.Interaction, a: int, b: int):
    await interaction.response.send_message(f"{a} + {b} = {a + b}")


@bot.event
async def on_member_join(member: discord.Member):
    channel = member.guild.system_channel
    if channel is not None:
        await channel.send(f"Welcome to {member.guild.name}, {member.mention}!")


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

What each part does:

  • Intents tell Discord which events the bot wants. The defaults cover slash commands; members adds join events.
  • CommandTree holds the slash commands, and tree.sync() registers them with Discord when the bot starts.
  • @bot.tree.command turns a function into a slash command. The type hints, a: int, become the command's options, and Discord checks the input for you.
  • on_member_join runs when someone joins and writes in the server's system channel, the one set under Server Settings, Overview.
  • The token comes from the DISCORD_TOKEN environment variable, never from the code.

Run it on your computer

Set the token for this terminal session, then start the bot. On macOS or Linux:

bash
export DISCORD_TOKEN="paste-your-token-here"
python bot.py

On Windows PowerShell:

powershell
$env:DISCORD_TOKEN="paste-your-token-here"
python bot.py

The terminal prints Online as YourBot#1234 in 1 server(s). Type / in your Discord server and the two commands appear. The first time, slash commands can take a few minutes to show up.

Host it for free on Torchlite

Your bot is offline as soon as you close the terminal. To keep it running:

  1. On Create your server, pick the Bot plan and Discord bot (Python).
  2. In the panel, open Startup and paste the token into Bot token.
  3. Open Files, delete the starter bot's files, and upload your bot.py and requirements.txt. Check that Main file under Startup is bot.py.
  4. Press Start in the Console. The server installs discord.py from requirements.txt and starts your bot.

To deploy from GitHub instead, so a push and a restart is your whole deploy, follow the Discord bot guide. The Bot plan gives 512 MB of RAM and asks for a Renew once every 24 hours; Discord bot hosting has the details.

Keep it healthy

A few habits save most of the trouble once real people use the bot:

  • Answer within 3 seconds. Discord shows "The application did not respond" if a command is not acknowledged in time. For anything slow, such as a web request or a database query, call await interaction.response.defer() first, then send the result with await interaction.followup.send(...).
  • Catch errors per command. Wrap risky code in try and except, and reply with a short message instead of letting the command fail silently. Print the error, so it shows up in the panel's Console.
  • Keep data on disk, not in memory. Everything in Python variables is lost when the bot restarts, and on a free server it restarts whenever a renew is missed. Store anything that matters in the MySQL database or an SQLite file.
  • Do not flood channels. Sending many messages in a loop runs into Discord's rate limits; discord.py waits them out, but your bot looks frozen in the meantime. Batch the text into one message instead.
  • Watch the memory. 512 MB is plenty, but caching every member of a big server adds up. Only switch on the intents you actually use.

Where to go next

  • More commands: copy the add block. str, int, float, bool, discord.Member and discord.Role all work as option types.
  • Commands only you can use: add @app_commands.default_permissions(administrator=True) above a command.
  • A database: your Bot server includes a MySQL database, or keep it simple with an SQLite file next to bot.py.
  • Split it up: when the file grows, move commands into separate files with discord.py's extensions and cogs.

Common problems

KeyError: 'DISCORD_TOKEN'

The environment variable is not set in this terminal, or on Torchlite the Bot token field is empty. Set it and start again.

PrivilegedIntentsRequired

The code asks for the members intent, but Server Members Intent is off in the Developer Portal. Switch it on, or remove the intents.members = True line and the welcome message.

The slash commands do not appear

Wait a few minutes after the first start, and check that the invite URL included applications.commands. Restart the bot once.

Stuck on a step?

Ask on the forum. Your question links back to this guide, so whoever answers knows where you are.

Ask about this guide

About the author

Torchlite team

The people who run Torchlite

We build and run the Torchlite panel and the nodes. Every guide is checked on our own servers before it goes up, and updated when the panel changes.

Found a mistake? Tell us on Discord

Related guides

All guides →