Back to Blog
Discord.js & Discord.py — How to Make a Discord Bot with Basic Commands (2025)
Tutorial June 18, 2025 15 min read dlist.space Team
discord.js discord.py discord bot commands javascript discord bot python discord bot discord bot tutorial

Discord.js & Discord.py — How to Make a Discord Bot with Basic Commands (2025)

In this comprehensive tutorial, you'll learn how to create a fully functional Discord bot using both discord.js (JavaScript/Node.js) and discord.py (Python). We'll cover setup, basic commands, and several useful features with complete code examples you can use right away.

discord.js and discord.py bot tutorial

discord.js vs discord.py — Which Should You Choose?

Both libraries are excellent choices for building Discord bots. Here's a quick comparison:

Feature discord.js discord.py
Language JavaScript (Node.js) Python
Difficulty Beginner-friendly Very beginner-friendly
Performance Excellent (async) Very good (async)
Community Very large Very large
Slash Commands Built-in support Built-in support

If you already know Python, start with discord.py. If you prefer JavaScript or want a larger ecosystem, go with discord.js.

Part 1: discord.js Bot Setup

Installation

Make sure you have Node.js 16 or higher installed. Then create a new folder for your project and run:

npm init -y
npm install discord.js dotenv

Create a .env file to store your bot token:

TOKEN=your_bot_token_here

Basic discord.js Bot with Multiple Commands

require('dotenv').config();
const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.GuildMembers,
    GatewayIntentBits.MessageContent,
  ],
});

const PREFIX = '!';

client.once('ready', () => {
  console.log(`✅ ${client.user.tag} is online!`);
  client.user.setActivity('dlist.space | !help', { type: 'WATCHING' });
});

client.on('messageCreate', async (message) => {
  if (message.author.bot) return;
  if (!message.content.startsWith(PREFIX)) return;

  const args = message.content.slice(PREFIX.length).trim().split(/ +/);
  const command = args.shift().toLowerCase();

  // !ping command
  if (command === 'ping') {
    const sent = await message.reply('Pinging...');
    sent.edit(`🏓 Pong! Latency: ${sent.createdTimestamp - message.createdTimestamp}ms`);
  }

  // !help command
  if (command === 'help') {
    const embed = new EmbedBuilder()
      .setTitle('📖 Command List')
      .setColor(0x5865F2)
      .addFields(
        { name: '!ping', value: 'Check bot latency', inline: true },
        { name: '!help', value: 'Show this menu', inline: true },
        { name: '!userinfo [@user]', value: 'Get user information', inline: true },
        { name: '!serverinfo', value: 'Get server information', inline: true },
        { name: '!kick [@user] [reason]', value: 'Kick a member (Admin)', inline: true },
        { name: '!ban [@user] [reason]', value: 'Ban a member (Admin)', inline: true },
        { name: '!clear [amount]', value: 'Delete messages (Mod)', inline: true },
        { name: '!avatar [@user]', value: "Get user's avatar", inline: true },
      )
      .setFooter({ text: 'Bot powered by discord.js' });
    message.reply({ embeds: [embed] });
  }

  // !userinfo command
  if (command === 'userinfo') {
    const target = message.mentions.members.first() || message.member;
    const embed = new EmbedBuilder()
      .setTitle(`👤 User Info — ${target.user.username}`)
      .setThumbnail(target.user.displayAvatarURL())
      .setColor(0x5865F2)
      .addFields(
        { name: 'Username', value: target.user.username, inline: true },
        { name: 'User ID', value: target.user.id, inline: true },
        { name: 'Joined Discord', value: target.user.createdAt.toDateString(), inline: true },
        { name: 'Joined Server', value: target.joinedAt.toDateString(), inline: true },
        { name: 'Roles', value: target.roles.cache.map(r => r.name).join(', ') || 'None' },
      );
    message.reply({ embeds: [embed] });
  }

  // !serverinfo command
  if (command === 'serverinfo') {
    const guild = message.guild;
    const embed = new EmbedBuilder()
      .setTitle(`🏠 ${guild.name}`)
      .setThumbnail(guild.iconURL())
      .setColor(0x5865F2)
      .addFields(
        { name: 'Members', value: guild.memberCount.toString(), inline: true },
        { name: 'Channels', value: guild.channels.cache.size.toString(), inline: true },
        { name: 'Roles', value: guild.roles.cache.size.toString(), inline: true },
        { name: 'Owner', value: `<@${guild.ownerId}>`, inline: true },
        { name: 'Created', value: guild.createdAt.toDateString(), inline: true },
      );
    message.reply({ embeds: [embed] });
  }

  // !avatar command
  if (command === 'avatar') {
    const target = message.mentions.users.first() || message.author;
    const embed = new EmbedBuilder()
      .setTitle(`🖼️ ${target.username}'s Avatar`)
      .setImage(target.displayAvatarURL({ size: 512 }))
      .setColor(0x5865F2);
    message.reply({ embeds: [embed] });
  }

  // !kick command
  if (command === 'kick') {
    if (!message.member.permissions.has('KickMembers')) {
      return message.reply('❌ You do not have permission to kick members.');
    }
    const target = message.mentions.members.first();
    if (!target) return message.reply('❌ Please mention a member to kick.');
    const reason = args.slice(1).join(' ') || 'No reason provided';
    await target.kick(reason);
    message.reply(`✅ ${target.user.username} was kicked. Reason: ${reason}`);
  }

  // !ban command
  if (command === 'ban') {
    if (!message.member.permissions.has('BanMembers')) {
      return message.reply('❌ You do not have permission to ban members.');
    }
    const target = message.mentions.members.first();
    if (!target) return message.reply('❌ Please mention a member to ban.');
    const reason = args.slice(1).join(' ') || 'No reason provided';
    await target.ban({ reason });
    message.reply(`✅ ${target.user.username} was banned. Reason: ${reason}`);
  }

  // !clear command
  if (command === 'clear') {
    if (!message.member.permissions.has('ManageMessages')) {
      return message.reply('❌ You do not have permission to delete messages.');
    }
    const amount = parseInt(args[0]);
    if (isNaN(amount) || amount < 1 || amount > 100) {
      return message.reply('❌ Please provide a number between 1 and 100.');
    }
    await message.channel.bulkDelete(amount + 1, true);
    const confirm = await message.channel.send(`✅ Deleted ${amount} messages.`);
    setTimeout(() => confirm.delete(), 3000);
  }
});

client.login(process.env.TOKEN);

Part 2: discord.py Bot Setup

Installation

Make sure you have Python 3.8 or higher installed. Install discord.py:

pip install discord.py python-dotenv

Create a .env file:

TOKEN=your_bot_token_here

Basic discord.py Bot with Multiple Commands

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

load_dotenv()
TOKEN = os.getenv('TOKEN')

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

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

@bot.event
async def on_ready():
    print(f'✅ {bot.user} is online!')
    await bot.change_presence(activity=discord.Activity(
        type=discord.ActivityType.watching,
        name='dlist.space | !help'
    ))

@bot.command()
async def ping(ctx):
    """Check bot latency"""
    await ctx.reply(f'🏓 Pong! Latency: {round(bot.latency * 1000)}ms')

@bot.command()
async def userinfo(ctx, member: discord.Member = None):
    """Get information about a user"""
    member = member or ctx.author
    embed = discord.Embed(title=f'👤 User Info — {member.name}', color=0x5865F2)
    embed.set_thumbnail(url=member.avatar.url)
    embed.add_field(name='Username', value=member.name, inline=True)
    embed.add_field(name='User ID', value=member.id, inline=True)
    embed.add_field(name='Joined Discord', value=member.created_at.strftime('%Y-%m-%d'), inline=True)
    embed.add_field(name='Joined Server', value=member.joined_at.strftime('%Y-%m-%d'), inline=True)
    roles = [r.name for r in member.roles if r.name != '@everyone']
    embed.add_field(name='Roles', value=', '.join(roles) or 'None', inline=False)
    await ctx.reply(embed=embed)

@bot.command()
async def serverinfo(ctx):
    """Get information about the server"""
    guild = ctx.guild
    embed = discord.Embed(title=f'🏠 {guild.name}', color=0x5865F2)
    if guild.icon:
        embed.set_thumbnail(url=guild.icon.url)
    embed.add_field(name='Members', value=guild.member_count, inline=True)
    embed.add_field(name='Channels', value=len(guild.channels), inline=True)
    embed.add_field(name='Roles', value=len(guild.roles), inline=True)
    embed.add_field(name='Created', value=guild.created_at.strftime('%Y-%m-%d'), inline=True)
    await ctx.reply(embed=embed)

@bot.command()
async def avatar(ctx, member: discord.Member = None):
    """Get a user's avatar"""
    member = member or ctx.author
    embed = discord.Embed(title=f"🖼️ {member.name}'s Avatar", color=0x5865F2)
    embed.set_image(url=member.avatar.url)
    await ctx.reply(embed=embed)

@bot.command()
@commands.has_permissions(kick_members=True)
async def kick(ctx, member: discord.Member, *, reason='No reason provided'):
    """Kick a member from the server"""
    await member.kick(reason=reason)
    await ctx.reply(f'✅ {member.name} was kicked. Reason: {reason}')

@bot.command()
@commands.has_permissions(ban_members=True)
async def ban(ctx, member: discord.Member, *, reason='No reason provided'):
    """Ban a member from the server"""
    await member.ban(reason=reason)
    await ctx.reply(f'✅ {member.name} was banned. Reason: {reason}')

@bot.command()
@commands.has_permissions(manage_messages=True)
async def clear(ctx, amount: int):
    """Delete a number of messages"""
    if amount < 1 or amount > 100:
        return await ctx.reply('❌ Please provide a number between 1 and 100.')
    await ctx.channel.purge(limit=amount + 1)
    msg = await ctx.send(f'✅ Deleted {amount} messages.')
    await asyncio.sleep(3)
    await msg.delete()

@kick.error
@ban.error
async def permission_error(ctx, error):
    if isinstance(error, commands.MissingPermissions):
        await ctx.reply('❌ You do not have permission to use this command.')

bot.run(TOKEN)

Adding Slash Commands (discord.js)

Slash commands are the modern way to interact with Discord bots. Here's how to add a basic slash command using discord.js:

const { SlashCommandBuilder } = require('discord.js');

// In your commands folder, create ping.js:
module.exports = {
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Check bot latency'),
  async execute(interaction) {
    await interaction.reply(`🏓 Pong! Latency: ${interaction.client.ws.ping}ms`);
  },
};

Next Steps

Now that you have a working bot with basic commands, here's what to learn next:

  • Database integration — Store user data with MongoDB or SQLite
  • Slash commands — Use modern application commands
  • Embeds and buttons — Create rich, interactive responses
  • Cogs/Modules — Organize commands into separate files
  • Economy system — Add currency and shop commands
  • Music playback — Stream audio from YouTube

Share Your Bot on dlist.space

Once your bot is ready, list it on dlist.space to share it with the Discord community and start growing your user base!