ESN Docs

Docs › Discord bots

Discord bot starter code library (Python, Node.js, TypeScript)

4 min readUpdated Nov 4, 2026

Three tested starter bots you can copy and paste: Python (discord.py), Node.js (discord.js) and TypeScript. Each one logs in and answers two slash commands. Then put it online 24/7 on ESN.

Which starter should I use? #

  • Python (discord.py): the easiest to read if you are new to coding. Great for most bots.
  • Node.js (discord.js): the most popular library, with the biggest set of examples online.
  • TypeScript (discord.js): discord.js with types, so your editor catches mistakes before you run the bot.

All three do the same thing: log in, register two slash commands (/ping and /hello) and answer them. Not sure? Read discord.py vs discord.js.

Before you start #

  1. Create a bot in the Discord Developer Portal, copy its token and invite it to your server with the bot and applications.commands scopes. Step by step: create a bot and get its token.
  2. Put the token in a file called .env, never in your code:
DISCORD_TOKEN=paste-your-token-here

Never share .env or post it in a screenshot. Anyone with the token controls your bot.

Python starter (discord.py) #

requirements.txt

discord.py
python-dotenv

main.py

import os

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

load_dotenv()  # reads DISCORD_TOKEN from the .env file
TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.default()
# intents.message_content = True  # only if you read normal messages (turn it on in the Developer Portal too)


class StarterBot(commands.Bot):
    async def setup_hook(self):
        synced = await self.tree.sync()  # registers your slash commands
        print(f"Synced {len(synced)} slash command(s)")


bot = StarterBot(command_prefix="!", intents=intents)


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


@bot.tree.command(name="ping", description="Check that the bot is alive")
async def ping(interaction: discord.Interaction):
    await interaction.response.send_message(f"Pong! {round(bot.latency * 1000)} ms")


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


bot.run(TOKEN)

Run it on your computer with pip install -r requirements.txt then python main.py. Slash commands can take a minute to show up the first time. If they don't, see slash commands not showing.

Node.js starter (discord.js) #

package.json

{
  "name": "my-discord-bot",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": { "discord.js": "^14.16.0", "dotenv": "^16.4.0" }
}

index.js

require("dotenv").config();
const { Client, Events, GatewayIntentBits, REST, Routes, SlashCommandBuilder } = require("discord.js");

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

const commands = [
  new SlashCommandBuilder().setName("ping").setDescription("Check that the bot is alive"),
  new SlashCommandBuilder().setName("hello").setDescription("Say hello"),
].map((command) => command.toJSON());

client.once(Events.ClientReady, async (readyClient) => {
  console.log(`Logged in as ${readyClient.user.tag}`);
  const rest = new REST().setToken(process.env.DISCORD_TOKEN);
  await rest.put(Routes.applicationCommands(readyClient.user.id), { body: commands });
  console.log(`Registered ${commands.length} slash commands`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;
  if (interaction.commandName === "ping") {
    await interaction.reply(`Pong! ${client.ws.ping} ms`);
  } else if (interaction.commandName === "hello") {
    await interaction.reply(`Hi ${interaction.user}!`);
  }
});

client.login(process.env.DISCORD_TOKEN);

Run it with npm install then node index.js.

TypeScript starter (discord.js) #

package.json

{
  "name": "my-ts-bot",
  "version": "1.0.0",
  "main": "dist/index.js",
  "scripts": { "build": "tsc", "start": "node dist/index.js" },
  "dependencies": { "discord.js": "^14.16.0", "dotenv": "^16.4.0" },
  "devDependencies": { "typescript": "^5.6.0", "@types/node": "^22.0.0" }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

src/index.ts

import "dotenv/config";
import {
  ChatInputCommandInteraction,
  Client,
  Events,
  GatewayIntentBits,
  REST,
  Routes,
  SlashCommandBuilder,
} from "discord.js";

const token = process.env.DISCORD_TOKEN;
if (!token) throw new Error("DISCORD_TOKEN is missing from .env");

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

const commands = [
  new SlashCommandBuilder().setName("ping").setDescription("Check that the bot is alive"),
  new SlashCommandBuilder().setName("hello").setDescription("Say hello"),
].map((command) => command.toJSON());

async function handle(interaction: ChatInputCommandInteraction): Promise<void> {
  if (interaction.commandName === "ping") {
    await interaction.reply(`Pong! ${client.ws.ping} ms`);
  } else if (interaction.commandName === "hello") {
    await interaction.reply(`Hi ${interaction.user}!`);
  }
}

client.once(Events.ClientReady, async (readyClient) => {
  console.log(`Logged in as ${readyClient.user.tag}`);
  const rest = new REST().setToken(token);
  await rest.put(Routes.applicationCommands(readyClient.user.id), { body: commands });
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (interaction.isChatInputCommand()) await handle(interaction);
});

client.login(token);

Build it with npm install then npm run build. That makes dist/index.js, which is the file you run.

Deploy it on ESN (24/7) #

  1. Get a bot plan. Pick one on the plans page. Bot hosting starts at $0.50/month and the server is set up right after checkout. Choose Discord bot (Python) or Discord bot (Node.js) to match your starter.
  2. Upload the files. In Files, upload your code, .env and requirements.txt or package.json to the top folder. Skip node_modules and venv. See uploading files.
  3. Set the start file. In Startup: Python uses main.py by default. Node.js uses index.js. For TypeScript, upload the dist folder and set Main JavaScript File to dist/index.js.
  4. Press Start. The server installs your packages on every start, then runs your bot. Look for the "Logged in as" line in the Console.

Full guides: host a Python bot, host a Node.js bot, host a TypeScript bot.

Next steps #

FAQ #

Are these starters free to use? #

Yes. Copy them, change them and use them in any bot you like.

Why is message_content commented out? #

Slash commands work without it. Only turn on the Message Content intent if your bot reads normal chat messages, and switch it on in the Developer Portal too, or the bot will not log in.

Which plan does a starter bot need? #

A small bot like this runs fine on the smallest bot plan. Bot hosting starts at $0.50/month, and you can move up later.

Will my bot stay online when my PC is off? #

Yes. On ESN Hosting the bot runs on our servers, not your computer, so it stays online 24/7.

Host your bot from $0.50/month →

Still stuck? Use the live chat on the panel or ask in the ESN Systems Discord. Last updated November 4, 2026.