Code your first Discord bot in Python
Beginner video tutorial and the full, tested discord.py 2 code: /ping, an /info embed, a welcome message and a safe /kick command, plus how to host the bot 24/7.
Watch the video #
The video starts from zero: installing Python and VS Code, the Python basics you need (variables, functions, if, async/await), creating the bot in the Discord Developer Portal, then writing every line of the bot below.
Download the code #
Download esn-first-discord-bot-python.zip (bot.py, requirements.txt, .env.example, .gitignore and a README).
Full bot.py #
Works with discord.py 2.3 or newer (tested with 2.7.1) on Python 3.10+. It has /ping, /info (an embed), a welcome message in a channel named welcome, and /kick with permission checks and error handling.
import os
import discord
from discord import app_commands
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")
intents = discord.Intents.default()
intents.members = True # needed for welcome messages
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def setup_hook():
synced = await bot.tree.sync()
print(f"Synced {len(synced)} slash commands")
@bot.event
async def on_ready():
print(f"Logged in as {bot.user}")
@bot.tree.command(name="ping", description="Check if the bot is online")
async def ping(interaction: discord.Interaction):
ms = round(bot.latency * 1000)
await interaction.response.send_message(f"Pong! {ms} ms")
@bot.tree.command(name="info", description="Show info about this server")
@app_commands.guild_only()
async def info(interaction: discord.Interaction):
guild = interaction.guild
embed = discord.Embed(
title=guild.name,
description="Here's some info about this server.",
color=0x6C5CE7,
)
embed.add_field(name="Members", value=guild.member_count)
created = guild.created_at.strftime("%b %d, %Y")
embed.add_field(name="Created", value=created)
if guild.icon:
embed.set_thumbnail(url=guild.icon.url)
embed.set_footer(text=f"Asked by {interaction.user.display_name}")
await interaction.response.send_message(embed=embed)
@bot.event
async def on_member_join(member: discord.Member):
channel = discord.utils.get(member.guild.text_channels, name="welcome")
if channel is None:
return
server = member.guild.name
embed = discord.Embed(
title="Welcome!",
description=f"Hey {member.mention}, welcome to **{server}**!",
color=0x6C5CE7,
)
embed.set_thumbnail(url=member.display_avatar.url)
await channel.send(embed=embed)
@bot.tree.command(name="kick", description="Kick a member from the server")
@app_commands.describe(member="Who to kick", reason="Reason for the kick")
@app_commands.default_permissions(kick_members=True)
@app_commands.checks.has_permissions(kick_members=True)
@app_commands.guild_only()
async def kick(
interaction: discord.Interaction,
member: discord.Member,
reason: str = "No reason given",
):
if member == interaction.user:
await interaction.response.send_message(
"You can't kick yourself.", ephemeral=True
)
return
if member.top_role >= interaction.guild.me.top_role:
await interaction.response.send_message(
"That member's role is higher than mine.", ephemeral=True
)
return
await member.kick(reason=reason)
await interaction.response.send_message(f"Kicked {member} | {reason}")
@kick.error
async def kick_error(
interaction: discord.Interaction, error: app_commands.AppCommandError
):
if isinstance(error, app_commands.MissingPermissions):
message = "You need the Kick Members permission."
else:
message = "Something went wrong. Check my permissions."
print(f"/kick error: {error}")
await interaction.response.send_message(message, ephemeral=True)
if not TOKEN:
raise SystemExit("DISCORD_TOKEN is missing. Put it in your .env file.")
bot.run(TOKEN)
requirements.txt and .env #
requirements.txt:
discord.py>=2.3
python-dotenv
.env (in the same folder as bot.py; never share it or upload it to GitHub):
DISCORD_TOKEN=paste-your-token-here
.gitignore:
.env
__pycache__/
Discord Developer Portal settings #
- Bot page: Reset Token, copy it into
.env. If it ever leaks, reset it again. - Bot page: switch on Server Members Intent (the welcome message needs it).
- OAuth2 → URL Generator: scopes
botandapplications.commands; permissions View Channels, Send Messages, Embed Links and Kick Members. Open the URL and pick your server. - For
/kick, the bot's role must be above the members it kicks (Server Settings → Roles).
More detail: How to create a Discord bot, get its token and invite it.
Run it #
python -m pip install -U discord.py python-dotenv
python bot.py
You should see Synced 3 slash commands and then Logged in as .... If you see Improper token has been passed, copy a fresh token into .env. If you see PrivilegedIntentsRequired, switch on Server Members Intent. See Common Discord bot errors.
Host it 24/7 on ESN #
- Pick a Discord bot plan at panel.esnoffical.com/plans (bot hosting from $0.50/month) and choose Discord bot (Python).
- In Files, upload
bot.py,requirements.txtand.envto the top folder. See Uploading files. - In Startup, set Main Python File to
bot.py. See Startup settings. - In Console, press Start. Each start runs
pip install -r requirements.txt, then your bot. Wait forLogged in as. - Stop the copy on your own computer, so only one copy of the bot runs.
Full guide: How to host a Python Discord bot on ESN Hosting. Want to go further? Try the 5-part Python course.
Still stuck? Use the live chat on the panel or ask in the ESN Systems Discord. Last updated November 3, 2026.