Skip to content

Python Guide

This guide walks you through deploying a Discord bot in Python on KataBump. We'll use the discord.py library, which is the reference for Python.

Prerequisites

  • Python 3.8+ installed on your machine
  • A Discord bot created on the Developer Portal
  • Your bot's token

Create Your Project

/my-discord-bot/
├── requirements.txt
├── main.py
├── config.py
└── cogs/
    └── moderation.py

1. Create a Virtual Environment

bash
mkdir my-discord-bot
cd my-discord-bot
python -m venv venv

# Activate the environment
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate

2. Create requirements.txt

discord.py==2.3.2

Alternative Libraries

Several forks exist (nextcord, pycord). The syntax is similar — adapt according to your choice.

3. Install Dependencies

bash
pip install -r requirements.txt

4. Create the Bot

Create a main.py file:

python
import discord
import os
from discord.ext import commands

# Configure intents
intents = discord.Intents.default()
intents.message_content = True  # Required to read message content

# Create the bot
bot = commands.Bot(
    command_prefix='!',
    intents=intents,
    help_command=None  # Disable default help command
)

# "on_ready" event
@bot.event
async def on_ready():
    print(f'Connected as {bot.user}')
    print(f'ID: {bot.user.id}')
    print('------')
    
    # Change bot status
    await bot.change_presence(
        activity=discord.Activity(
            type=discord.ActivityType.watching,
            name='!help commands'
        )
    )

# !ping command
@bot.command()
async def ping(ctx):
    """Responds with Pong!"""
    latency = round(bot.latency * 1000)
    await ctx.send(f'Pong! `{latency}ms`')

# !hello command
@bot.command()
async def hello(ctx):
    """Greets the user"""
    await ctx.send(f'Hello {ctx.author.mention}!')

# Custom !help command
@bot.command()
async def help(ctx):
    """Displays help"""
    embed = discord.Embed(
        title='Available Commands',
        color=discord.Color.blue()
    )
    embed.add_field(name='!ping', value='Responds with Pong!', inline=False)
    embed.add_field(name='!hello', value='Greets the user', inline=False)
    embed.add_field(name='!help', value='Displays this help', inline=False)
    
    await ctx.send(embed=embed)

# Error handling
@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.CommandNotFound):
        return  # Ignore unknown commands
    
    if isinstance(error, commands.MissingPermissions):
        await ctx.send('You do not have permission to use this command.')
        return
    
    # Log other errors
    print(f'Error: {error}')

# Get token from environment variables
TOKEN = os.getenv('DISCORD_TOKEN')

if not TOKEN:
    print('Error: DISCORD_TOKEN variable is not defined')
    exit(1)

# Start the bot
bot.run(TOKEN)

5. Set Up Environment Variables

Create a .env file at the root of your project:

DISCORD_TOKEN=your_token_here

Install python-dotenv and add it to requirements.txt:

bash
pip install python-dotenv
discord.py==2.3.2
python-dotenv==1.0.0

Add the following at the top of main.py:

python
from dotenv import load_dotenv
load_dotenv()

.env on KataBump

The .env file is used both locally and on KataBump. Upload it to your server along with your other files. See Set Environment Variables for more details.

6. Test Locally (optional)

bash
python main.py

::: success Works Locally? If your bot works locally, it will work on KataBump! :::

Deploy to KataBump

1. Create a Python Server

  1. Log in to dashboard.katabump.com
  2. Click "Create a server"
  3. Choose Python
  4. Name your server
  5. Click "Create"
  6. On control.katabump.com, go to the "Startup" tab to select your Python version (recommended: 3.10 or 3.11)

2. Upload Files

Via Web

  1. Create a ZIP file containing your project files
  2. Go to the "Files" tab on control.katabump.com
  3. Drag and drop your ZIP file
  4. Unzip the file on the panel
  5. Make sure requirements.txt and main.py are at the root

Via SFTP

  1. Retrieve your SFTP credentials in the "Settings" section
  2. Connect with FileZilla
  3. Upload files to /home/container

3. Configure Startup

  1. Go to the "Startup" tab on control.katabump.com
  2. Set the "PY FILE" to your entry point (e.g. main.py)
  3. Save

Environment Variables

Your .env file (containing your DISCORD_TOKEN) should already be included in your uploaded files. If not, upload it via the file manager or SFTP.

4. Start the Server

  1. Go to the "Console" tab
  2. Click "Start"
  3. Wait for dependency installation (automatic pip install)
  4. You should see:
    Connected as MyBot#1234
    ID: 123456789
    ------

::: success Bot Online! Your bot is now connected 24/7! :::

Slash Commands

Slash commands are the new Discord standard.

Example with discord.py

python
import discord
from discord import app_commands
import os

class MyBot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.tree = app_commands.CommandTree(self)
    
    async def setup_hook(self):
        # Sync commands (do this once)
        await self.tree.sync()
        print('Slash commands synced')
    
    async def on_ready(self):
        print(f'Connected as {self.user}')

bot = MyBot()

# /ping slash command
@bot.tree.command(name="ping", description="Responds with Pong!")
async def ping(interaction: discord.Interaction):
    latency = round(bot.latency * 1000)
    await interaction.response.send_message(f'Pong! `{latency}ms`')

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

# Command with parameter
@bot.tree.command(name="echo", description="Repeats a message")
@app_commands.describe(message="The message to repeat")
async def echo(interaction: discord.Interaction, message: str):
    await interaction.response.send_message(f'{message}')

# Admin-only command
@bot.tree.command(name="admin", description="Admin command")
@app_commands.checks.has_permissions(administrator=True)
async def admin(interaction: discord.Interaction):
    await interaction.response.send_message('Admin command executed!', ephemeral=True)

@admin.error
async def admin_error(interaction: discord.Interaction, error):
    if isinstance(error, app_commands.MissingPermissions):
        await interaction.response.send_message('You must be an administrator!', ephemeral=True)

bot.run(os.getenv('DISCORD_TOKEN'))

Synchronization

Slash commands must be synced with Discord. For large bots, do it manually or via a specific command rather than at startup.

For large projects, use Cogs to organize your code:

/my-bot/
├── requirements.txt
├── main.py
└── cogs/
    ├── __init__.py
    ├── moderation.py
    └── fun.py

main.py

python
import discord
from discord.ext import commands
import os
import asyncio

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

bot = commands.Bot(command_prefix='!', intents=intents)

async def load_extensions():
    for filename in os.listdir('./cogs'):
        if filename.endswith('.py'):
            await bot.load_extension(f'cogs.{filename[:-3]}')
            print(f'Extension {filename} loaded')

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

async def main():
    async with bot:
        await load_extensions()
        await bot.start(os.getenv('DISCORD_TOKEN'))

asyncio.run(main())

cogs/moderation.py

python
import discord
from discord.ext import commands
from discord import app_commands

class Moderation(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
    
    @commands.command()
    @commands.has_permissions(kick_members=True)
    async def kick(self, ctx, member: discord.Member, *, reason=None):
        """Kicks a member"""
        await member.kick(reason=reason)
        await ctx.send(f'{member.mention} has been kicked.')
    
    @app_commands.command(name="ban", description="Bans a member")
    @app_commands.describe(member="The member to ban", reason="The reason for the ban")
    @app_commands.checks.has_permissions(ban_members=True)
    async def ban_slash(self, interaction: discord.Interaction, member: discord.Member, reason: str = None):
        await member.ban(reason=reason)
        await interaction.response.send_message(f'{member.mention} has been banned.')

async def setup(bot):
    await bot.add_cog(Moderation(bot))

Best Practices

Error Handling

python
import logging

# Logging configuration
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

@bot.event
async def on_error(event, *args, **kwargs):
    logging.error(f'Error in {event}: ', exc_info=True)

@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.CommandNotFound):
        return
    
    logging.error(f'Command error: {error}')

Database Connection

python
import sqlite3
from contextlib import closing

# SQLite (local file)
DB_PATH = '/home/container/database.db'

def get_db():
    return sqlite3.connect(DB_PATH)

# Usage example
@bot.command()
async def addquote(ctx, *, text):
    with closing(get_db()) as db:
        db.execute('INSERT INTO quotes (text, author) VALUES (?, ?)', 
                   (text, ctx.author.name))
        db.commit()
    await ctx.send('Quote added!')

Database

For SQLite, data is stored in the server folder. Paid plans include MariaDB databases — see Plans for details.

Memory Optimization

python
import gc

@bot.event
async def on_ready():
    # Force garbage collection on startup
    gc.collect()
    print(f'Connected as {bot.user}')

# Periodic cleanup (optional)
@tasks.loop(hours=1)
async def cleanup():
    gc.collect()
    print('Memory cleanup done')

Free Plan Memory

The free plan provides 308 MB RAM. Optimize your code to stay within limits.

Next Steps

Official KataBump Documentation