Creating your first Discord bot using TypeScript
Tags: discord, tutorial, typescript, node
Published: 2023-03-19 08:03:18

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:

- [A Discord account](https://discord.com/register)

- [Node.js](https://nodejs.org/en/download) installed on your computer.

## Setting up a new bot

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

1\. Go to the [Discord Developer Portal](https://discord.com/developers/applications) and create a new application.

![Discord Developer Portal screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hf7gh98m4bu7x5iwztd9.png)

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

![New Discord Application dialog screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdnc47txw62petdd2n3m.png)

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

![Discord Application "General Information" page screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d2bgh9llauee9gqmolhr.png)

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

![Discord Application "Bot" page screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1l6qinjpw0qz1pzgc633.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ka7gnm0scg83wmze9gqt.png)

## 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:

```bash
npm install discord.js dotenv
```

4\. Install the following dev dependencies:
```bash
npm install -D typescript tsx tsup
```

5\. Add some scripts to your `package.json`:

```json
{
  "dev": "tsx watch src/index.ts",
  "start": "node dist/index.js",
  "build": "tsup src/index.ts --minify"
}
```

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

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

## 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>
```

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

```ts
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,
};
```

## 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.

```ts
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!");
}
```

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

```ts
import * as ping from "./ping";

export const commands = {
  ping,
};
```

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:

```ts
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);
  }
}
```

## 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:
```ts
import { Client } from "discord.js";

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

2\. Add a console.log when the bot is ready:
```ts
client.once("ready", () => {
  console.log("Discord bot is ready! 🤖");
});
```

3\. Deploy commands when new guild has been created:
```ts
client.on("guildCreate", async (guild) => {
  await deployCommands({ guildId: guild.id });
});
```

4\. Run corresponding command when new user interaction has been created:
```ts
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);
  }
});
```

5\. Log in the client using your token:
```ts
client.login(config.DISCORD_TOKEN);
```

After all steps, your file should look like that: 
```ts
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);
```

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