DEV Community

Cover image for Creating your first Discord bot using TypeScript
Fellipe Utaka
Fellipe Utaka

Posted on • Updated on

Creating your first Discord bot using TypeScript

Discord is a popular platform for communication among gamers and communities. One of the key features of Discord is its ability to host bots, which can automate tasks and provide helpful services for users. In this tutorial, we'll go through the steps of creating your first Discord bot using TypeScript.

Prerequisites

Before we begin, you'll need to have the following:

Setting up a new bot

To create a new bot, you'll need to follow these steps:

1. Go to the Discord Developer Portal and create a new application.

Discord Developer Portal screenshot

2. Give your application a name and click "Create".

New Discord Application dialog screenshot

3. Copy your Application ID selecting and copying or just click on "Copy" button.

Discord Application "General Information" page screenshot

4. On the left-hand side, click on "Bot" and then click "Add Bot".

Discord Application "Bot" page screenshot

5. Customize your bot's name and profile picture as desired, and then click "Save Changes".

6. Under the "Token" section, click "Copy" to copy the bot's token to your clipboard. This token will be used later to authenticate your bot.

Discord Application "Bot" page with a click on "Copy" button under the "Token" section screenshot

Setting up the project

Now that we have our bot's token and Application Client ID, let's set up our TypeScript project. Follow these steps:

1. Create a new directory for your project and navigate into it.

2. Run npm init to initialize your project with a package.json file.

3. Install the following packages:

npm install discord.js dotenv
Enter fullscreen mode Exit fullscreen mode

4. Install the following dev dependencies:

npm install -D typescript tsx tsup
Enter fullscreen mode Exit fullscreen mode

5. Add some scripts to your package.json:

{
  "dev": "tsx watch src/index.ts",
  "start": "node dist/index.js",
  "build": "tsup src/index.ts --minify"
}
Enter fullscreen mode Exit fullscreen mode

6. Create a tsconfig.json file to configure TypeScript:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./dist",
    "removeComments": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictNullChecks": true,
    "skipLibCheck": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Setting up environment variables

To keep sensitive information like our bot's token secure, we'll use environment variables. Follow these steps to set them up:

Create a .env file in the root of your project directory.

Add the following line to the file, replacing DISCORD_TOKEN with the bot token you copied earlier and DISCORD_CLIENT_ID with the bot client id:

DISCORD_TOKEN=<DISCORD_TOKEN>
DISCORD_CLIENT_ID=<DISCORD_CLIENT_ID>
Enter fullscreen mode Exit fullscreen mode

Now, create a src folder and a config.ts file to get them.

import dotenv from "dotenv";

dotenv.config();

const { DISCORD_TOKEN, DISCORD_CLIENT_ID } = process.env;

if (!DISCORD_TOKEN || !DISCORD_CLIENT_ID) {
  throw new Error("Missing environment variables");
}

export const config = {
  DISCORD_TOKEN,
  DISCORD_CLIENT_ID,
};
Enter fullscreen mode Exit fullscreen mode

Creating your first command

In this tutorial, I'm going to create a command called ping that will return "Pong!". To do that, you just have to export a SlashCommand data containing all information about your command and a function to handle it. Create a commands folder inside the src folder and create a ping.ts file inside it.

import { CommandInteraction, SlashCommandBuilder } from "discord.js";

export const data = new SlashCommandBuilder()
  .setName("ping")
  .setDescription("Replies with Pong!");

export async function execute(interaction: CommandInteraction) {
  return interaction.reply("Pong!");
}
Enter fullscreen mode Exit fullscreen mode

You can create a index.ts file inside the commands folder to export all commands in one file.

import * as ping from "./ping";

export const commands = {
  ping,
};
Enter fullscreen mode Exit fullscreen mode

Great! You did it! But there is a problem. How the servers which the bot is added know the commands it has? To solve this problem, we need to register all the commands to each server.

Deploying your command

To deploy your commands, we will create a deploy-commands.ts file to register the commands in the corresponding server. Let's do it:

import { REST, Routes } from "discord.js";
import { config } from "./config";
import { commands } from "./commands";

const commandsData = Object.values(commands).map((command) => command.data);

const rest = new REST({ version: "10" }).setToken(config.DISCORD_TOKEN);

type DeployCommandsProps = {
  guildId: string;
};

export async function deployCommands({ guildId }: DeployCommandsProps) {
  try {
    console.log("Started refreshing application (/) commands.");

    await rest.put(
      Routes.applicationGuildCommands(config.DISCORD_CLIENT_ID, guildId),
      {
        body: commandsData,
      }
    );

    console.log("Successfully reloaded application (/) commands.");
  } catch (error) {
    console.error(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Initializing your bot

To initialize your bot, you can create a bot.ts or index.ts file inside src folder. In this tutorial, I will create a index.ts instead.

For the bot works properly, you need to follow these steps:

1. Create a new Discord Client and set its intents to determine which events the bot will receive information about. In this example, the bot will receive information about guilds, guild messages, and direct messages:

import { Client } from "discord.js";

const client = new Client({
  intents: ["Guilds", "GuildMessages", "DirectMessages"],
});
Enter fullscreen mode Exit fullscreen mode

2. Add a console.log when the bot is ready:

client.once("ready", () => {
  console.log("Discord bot is ready! 🤖");
});
Enter fullscreen mode Exit fullscreen mode

3. Deploy commands when new guild has been created:

client.on("guildCreate", async (guild) => {
  await deployCommands({ guildId: guild.id });
});
Enter fullscreen mode Exit fullscreen mode

4. Run corresponding command when new user interaction has been created:

client.on("interactionCreate", async (interaction) => {
  if (!interaction.isCommand()) {
    return;
  }
  const { commandName } = interaction;
  if (commands[commandName as keyof typeof commands]) {
    commands[commandName as keyof typeof commands].execute(interaction);
  }
});
Enter fullscreen mode Exit fullscreen mode

5. Log in the client using your token:

client.login(config.DISCORD_TOKEN);
Enter fullscreen mode Exit fullscreen mode

After all steps, your file should look like that:

import { Client } from "discord.js";
import { config } from "./config";
import { commands } from "./commands";
import { deployCommands } from "./deploy-commands";

const client = new Client({
  intents: ["Guilds", "GuildMessages", "DirectMessages"],
});

client.once("ready", () => {
  console.log("Discord bot is ready! 🤖");
});

client.on("guildCreate", async (guild) => {
  await deployCommands({ guildId: guild.id });
});

client.on("interactionCreate", async (interaction) => {
  if (!interaction.isCommand()) {
    return;
  }
  const { commandName } = interaction;
  if (commands[commandName as keyof typeof commands]) {
    commands[commandName as keyof typeof commands].execute(interaction);
  }
});

client.login(config.DISCORD_TOKEN);
Enter fullscreen mode Exit fullscreen mode

Very good! Now you just have to run npm run dev and add your bot to some server and use /ping in some text channel.

If you just want to clone the repository, follow the link below to the repository on GitHub. I would be very happy if you leave a star.

https://github.com/fellipeutaka/discord-bot-template

Top comments (3)

Collapse
 
enxtur profile image
Enkhtur

Nice

Collapse
 
cooldudeno13026 profile image
CooldudeNo13026

Thanks for the tutorial, I finally found a code example that works, thanks!

Collapse
 
zkriguer profile image
zKriguer

That's awesome! Thanks