Blog/Software Development

How to Build a Discord Bot Using Python (Step-by-Step Guide)

Software engineer sitting at a desk typing on a desktop computer

Discord bots are a fantastic way to enhance your server, whether you're looking to streamline tasks, introduce fun features, or simply add a touch of entertainment. In this guide, we’ll walk you through the steps to create a Discord bot in Python, complete with a fun number guessing game to engage your users. We’ll even add some fun extras like motivational messages, a fun fact if the user guesses the number using API requests, and a UI component to make the bot more interactive.

What you’ll learn from this guide:

In this guide, you’ll learn:

  • How to set up Python and PyCharm as your coding environment.
  • How to create a basic Discord bot using Python’s discord library.
  • How to set up bot permissions and invite the bot to your server.
  • How to create a number guessing game that responds to commands with “/”.
  • How to make the bot mention the user and give feedback on whether their guess is too high or too low.
  • How to add a fun fact using API, so the bot shares a cool fact about the guessed number.
  • How to set up basic UI components (like buttons) to make interactions more fun.

Prerequisites

Before we start coding, here’s what you’ll need:

  1. Python installed on your computer. If you don’t have it yet, you can download it from python.org. (https://www.python.org/downloads/)

After installing, open up terminal and check if Python is properly installed by typing:

python --version

You should see a version number.

  1. We’ll be using PyCharm IDE, which makes writing code in Python easier. PyCharm is beginner friendly with many great features like virtual environment creation, that helps you keep track of installed libraries and organize your files. You can get the free version of PyCharm (Community Edition) from JetBrains (https://www.jetbrains.com/pycharm/download/).
  2. A Discord account and access to the Discord Developer Portal (https://www.discord.com/developers/applications). This is where we’ll create and manage our bot.

Step 1: Setting up your coding environment in PyCharm

Alright, let’s set up our project in PyCharm and get everything ready for coding.

Creating a project

1. Open PyCharm and click on “New Project

2. Name your project and choose where you want to save it.

3. Create a virtual environment. PyCharm will ask you if you’d like to set up a new virtual environment. In the Interpreter type choose “Project venv”, and make sure the Python version is set to your Python installation. Click Create, and PyCharm will open a new window with your project. Virtual environments are useful, especially when working with projects that have specific library requirements. They keep your libraries contained to this one project, which helps avoid conflicts with other Python projects.

Installing required libraries

Next, let’s install the libraries our bot will need:

  • discord.py: This is a Python wrapper for the Discord API, allowing us to connect our bot and interact with Discord.
  • requests: A library for making HTTP requests, which we’ll use to get fun facts for the bot.
  • python-dotenv: This library helps us securely load environment variables (like our bot token) from a file, so we don’t hardcode sensitive information into our code.

Open the Terminal in PyCharm. You can find it at the bottom left sidebar. Make sure your virtual environment is activated (you should see (.venv) in the terminal).

Run the following command:

pip install discord.py requests python-dotenv

Once everything is installed, we’re ready to set up our bot.

Step 2: Creating your bot in the Discord Developer Portal

Head over to the Discord Developer Portal (https://www.discord.com/developers/applications) to set up the bot itself.

Create a new application

Click on New Application in the top right corner. Give it a name, like “Number Guessing Bot,” and hit Create.

Add a bot to your application

Go to the Bot section on the left sidebar. Here you can change your bot’s username, icon, banner. Next, click on the “Reset Token” You should see your bot's token. The bot token is your bot’s unique key to access Discord, so keep it private to ensure its security. Now, go back to PyCharm and right click on your project’s main folder and create a new file “.env”. Copy the token and paste it into a file called .env in your project folder like this:

DISCORD_TOKEN=thetoken

This file will securely store the token, so it’s not exposed in your code.

Set up bot permissions

To make sure our bot works properly, let’s set up its permissions. While still on the Bot section, scroll down and check the “Message Content Intent” and “Server Members Intent”. Then go to the OAuth2 section and scroll down to URL Generator.

Under Scopes select “Bot” and then choose:

  • Send Messages: This will allow the bot to send messages.
  • Read Message History: This will allow the bot to access messages in the channels it’s in.
  • Mention Everyone: This will allow the bot to mention users, which we will need for congratulating users by name.

Then, choose “Guild Install”, generate the link, paste it into your browser and invite the bot to your server.

Step 3: Writing the bot code

Now comes the fun part: coding the bot!

Setting up the bot with imports and initial configurations

The first thing you need to do is set up the necessary libraries and initialize the bot. The imports are the essential building blocks for the bot to work with Discord’s API, manage commands, and load the bot’s token securely.

In PyCharm, create a new file main.py and add the following code:

import discord
import os
import random
import requests
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.default()
intents.messages = True
intents.members = True
intents.message_content = True

bot = commands.Bot(command_prefix="/", intents=intents)
active_games = {}

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

We import ‘discord’ for the Discord API, ‘os’ for environment variables, ‘random’ for generating random numbers, ‘requests’ for making HTTP calls, and ‘dotenv’ to securely load the token from an .env file.

The bot will use Intents to specify which events (like reading messages) the bot is allowed to interact with.

The on_ready event function will get called when the bot successfully logs in. It will print the message provided in your PyCharm console. (We will need to add bot.run(TOKEN)line at the end of the code in order for it to run)

Making the /startGuess <number> command

The /startGuess <number> command starts the game by selecting a random number within a user specified range.

@bot.command(name="startGuess")
async def start_guess(ctx, max_range: int):
    if ctx.author.id in active_games:
        await ctx.send("You already have an active game! Finish it before starting a new one.")
        return

    if max_range < 1:
        await ctx.send("Please provide a positive number greater than 1.")
        return

    secret_number = random.randint(1, max_range)
    active_games[ctx.author.id] = {
        "secret_number": secret_number,
        "attempts": 0
    }
    await ctx.send(f"Game started! I'm thinking of a number between 1 and {max_range}. Start guessing with `/guess <your number>`.")

The @bot.command tells Discord to treat this function as a command. In our case, it’s the /startGuess command. The bot checks if the user already has an active game. If they do, it informs them to finish the current game. Then, it checks if the range is valid (greater than 1). If the range is valid, the bot generates a random number between 1 and the user's input max_range and stores this information in the active_games, with the key of the user's ID.

Adding the guess command to handle user guesses

Add the /guess command to allow users to submit guesses and get feedback.

@bot.command(name="guess")
async def guess(ctx, user_guess: int):
    game = active_games.get(ctx.author.id)

    if game:
        game["attempts"] += 1
        secret_number = game["secret_number"]

        if user_guess < secret_number:
            await ctx.send(f"{ctx.author.mention}, your guess is too low. Try again!")
        elif user_guess > secret_number:
            await ctx.send(f"{ctx.author.mention}, your guess is too high. Try again!")
        else:
            attempts = game["attempts"]
            response = requests.get(f"http://numbersapi.com/{secret_number}/trivia")
            trivia = response.text if response.status_code == 200 else "No trivia available."

            await ctx.send(
                f"🎉 Congrats {ctx.author.mention}! You guessed the number {secret_number} in {attempts} tries! Fun fact: {trivia}"
            )
            del active_games[ctx.author.id]
    else:
        await ctx.send(f"{ctx.author.mention}, you haven't started a game yet! Type `/startGuess` to begin.")

As in the previous function @bot.command, this time it’s with the name “guess”. The /guess command will take the user's guess. The bot will also check if the user has an active game by looking up their user ID in the active_games dictionary. The bot will compare the user’s guess to the secret number.

If too low, it will inform the user to guess higher. If too high, it will ask them to guess lower. If the guess is correct the game will end and a fun fact about the number will be given using the Numbers API as well as a congrats message.

After the user wins, the game data will get removed from active_games.

Adding UI buttons and changing /startGuess <number> to /startGuess

Now we’ll modify /startGuess to remove the need for a numeric range by adding a button-based UI. This will replace /startGuess <number> with just /startGuess.

from discord.ui import View, Button

pre_game_messages = [
    "Get ready to guess!",
    "Let's see if you can guess the number!",
    "The guessing game is about to begin!"
]

class StartGameView(View):
    def __init__(self, ctx):
        super().__init__()
        self.ctx = ctx

    @discord.ui.button(label="Start", style=discord.ButtonStyle.green)
    async def start_button(self, interaction: discord.Interaction, button: Button):
        if self.ctx.author.id in active_games:
            await interaction.response.send_message(
                "You already have an active game! Finish it before starting a new one.",
                ephemeral=True
            )
            return
        await interaction.response.send_message("Enter your desired range in the pop-up modal.", ephemeral=True)

    @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red)
    async def cancel_button(self, interaction: discord.Interaction, button: Button):
        await interaction.response.send_message("Game setup canceled.", ephemeral=True)

@bot.command(name="startGuess")
async def start_guess_ui(ctx):
    pre_game_message = random.choice(pre_game_messages)
    await ctx.send(pre_game_message)

    view = StartGameView(ctx)
    await ctx.send("Ready to start the game?", view=view)

Make sure to update your /startGuess command to the new one, as we updated it to now work with the UI. This class creates a view that is a container for UI elements like buttons. It includes two buttons:

Start Button: Starts the game and asks the user to input the range.

Cancel Button: Cancels the game.

Adding the modal input for custom range

Now we’ll add a modal that prompts the user to input their desired maximum range, making the setup fully interactive.

from discord.ui import Modal, TextInput

class RangeInputModal(Modal):
    def __init__(self, ctx):
        super().__init__(title="Set Game Range")
        self.ctx = ctx
        self.range_input = TextInput(label="Enter the maximum range", placeholder="e.g., 100")
        self.add_item(self.range_input)

    async def on_submit(self, interaction: discord.Interaction):
        try:
            max_range = int(self.range_input.value)
            if max_range < 1:
                await interaction.response.send_message("Please enter a number greater than 1.", ephemeral=True)
                return
        except ValueError:
            await interaction.response.send_message("Please enter a valid number.", ephemeral=True)
            return

        secret_number = random.randint(1, max_range)
        active_games[self.ctx.author.id] = {
            "secret_number": secret_number,
            "attempts": 0
        }
        await interaction.response.send_message(
            f"Game started! I'm thinking of a number between 1 and {max_range}. Start guessing with `/guess <your number>`."
        )

@discord.ui.button(label="Start", style=discord.ButtonStyle.green)
async def start_button(self, interaction: discord.Interaction, button: Button):
    if self.ctx.author.id in active_games:
        await interaction.response.send_message(
            "You already have an active game! Finish it before starting a new one.",
            ephemeral=True
        )
        return
    modal = RangeInputModal(self.ctx)
    await interaction.response.send_modal(modal)

It is important to update our “@discord.ui.button(label="Start"” Start button to the updated one. This new one contains the modal interaction which will allow us to input the number range. The on_submit method will be triggered when the user submits the modal. The input will get processed to start the game with the chosen range. The bot also checks if the input is valid.

import discord
import os
import random
import requests
from discord.ext import commands
from dotenv import load_dotenv
from discord.ui import Modal, TextInput, View, Button

load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.default()
intents.messages = True
intents.members = True
intents.message_content = True

bot = commands.Bot(command_prefix="/", intents=intents)
active_games = {}

pre_game_messages = [
    "Get ready to guess!",
    "Let's see if you can guess the number!",
    "The guessing game is about to begin!"
]


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


class RangeInputModal(Modal):
    def __init__(self, ctx):
        super().__init__(title="Set Game Range")
        self.ctx = ctx
        self.range_input = TextInput(label="Enter the maximum range", placeholder="e.g., 100")
        self.add_item(self.range_input)

    async def on_submit(self, interaction: discord.Interaction):
        try:
            max_range = int(self.range_input.value)
            if max_range < 1:
                await interaction.response.send_message("Please enter a number greater than 1.", ephemeral=True)
                return
        except ValueError:
            await interaction.response.send_message("Please enter a valid number.", ephemeral=True)
            return

        secret_number = random.randint(1, max_range)
        active_games[self.ctx.author.id] = {
            "secret_number": secret_number,
            "attempts": 0
        }
        await interaction.response.send_message(
            f"Game started! I'm thinking of a number between 1 and {max_range}. Start guessing with `/guess <your number>`."
        )


class StartGameView(View):
    def __init__(self, ctx):
        super().__init__()
        self.ctx = ctx

    @discord.ui.button(label="Start", style=discord.ButtonStyle.green)
    async def start_button(self, interaction: discord.Interaction, button: Button):
        if self.ctx.author.id in active_games:
            await interaction.response.send_message(
                "You already have an active game! Finish it before starting a new one.",
                ephemeral=True
            )
            return
        modal = RangeInputModal(self.ctx)
        await interaction.response.send_modal(modal)

    @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red)
    async def cancel_button(self, interaction: discord.Interaction, button: Button):
        await interaction.response.send_message("Game setup canceled.", ephemeral=True)


@bot.command(name="startGuess")
async def start_guess_ui(ctx):
    pre_game_message = random.choice(pre_game_messages)
    await ctx.send(pre_game_message)

    view = StartGameView(ctx)
    await ctx.send("Ready to start the game?", view=view)


@bot.command(name="guess")
async def guess(ctx, user_guess: int):
    game = active_games.get(ctx.author.id)

    if game:
        game["attempts"] += 1
        secret_number = game["secret_number"]

        if user_guess < secret_number:
            await ctx.send(f"{ctx.author.mention}, your guess is too low. Try again!")
        elif user_guess > secret_number:
            await ctx.send(f"{ctx.author.mention}, your guess is too high. Try again!")
        else:
            attempts = game["attempts"]
            response = requests.get(f"http://numbersapi.com/{secret_number}/trivia")
            trivia = response.text if response.status_code == 200 else "No trivia available."

            await ctx.send(
                f"🎉 Congrats {ctx.author.mention}! You guessed the number {secret_number} in {attempts} tries! Fun fact: {trivia}"
            )
            del active_games[ctx.author.id]
    else:
        await ctx.send(f"{ctx.author.mention}, you haven't started a game yet! Type `/startGuess` to begin.")


bot.run(TOKEN)

Now, to run the bot, simply run ‘python main.py’ in your PyCharm console. To stop the bot press CTRL + C.

Here is how the Bot looks in action:

Image of Discord Bot

Pressing “Cancel” will show this:

Screen showing 'cancel' message in Discord Bot.

Pressing “Start” will open the modal:

Set game range screen in Discord Bot

Full game with range “3”:

Discord Bot showing screen for GuessingNumber app.

Starting a game while another one is in progress:

Screen showing options for starting new game in Discord Bot

Congratulations—you’ve built a Discord Bot using Python!

We hope this guide has been a fun and insightful introduction to building your very own Discord bot using Python. By now, you’ve set up your coding environment, created a bot from scratch, and even added some interactive features like number guessing and fun fact sharing. Whether you’re planning to build more complex bots or just wanted to dip your toes into Python programming, you’ve taken a big step forward. So, what will your next bot do? The possibilities are endless.

Do you have a project that requires software development skills or knowledge of specific programming languages? Or perhaps you’ve developed software that needs to be tested? Contact us to learn more about our software testing and development services.

QA engineer having a video call with 5-start rating graphic displayed above

Deliver a product made to impress

Build a product that stands out by implementing best software QA practices.

Get started today