Customization Guide

In this guide you will learn how to customize your bot to change the features or add new ones.

Project Structure

Everything in the bot is contained within a src folder, this contains all of the source code for the bot. The src folder is then divided into several folders:

📦src // Main Source File
 ┣ 📂commands // Source for all commands
 ┃ ┣ 📜config.ts
 ┃ ┣ 📜gCreate.ts
 ┃ ┣ 📜gEnd.ts
 ┃ ┗ 📜gReRoll.ts
 ┣ 📂database // Database Management 
 ┃ ┣ 📂data // Actual Database
 ┃ ┃ ┣ 📜database.db
 ┃ ┃ ┣ 📜database.db-shm
 ┃ ┃ ┗ 📜database.db-wal
 ┃ ┣ 📜dbManager.ts
 ┃ ┣ 📜guildManager.ts
 ┃ ┗ 📜gwManager.ts
 ┣ 📂events // Event manager
 ┃ ┣ 📜guildCreate.ts
 ┃ ┣ 📜interactionCreate.ts
 ┃ ┗ 📜ready.ts
 ┣ 📂handlers // Starting routines
 ┃ ┣ 📜Command.ts
 ┃ ┗ 📜Event.ts
 ┣ 📜functions.ts // Utility Functions
 ┣ 📜index.ts // Initial File
 ┗ 📜types.d.ts

Editing Existing Commands

Commands in the bot are stored in the 📂src/commands folder. Each command is implemented as a separate file, following the structure of Discord.js interactions.

Step 1: Locate the Command File

For example, the gCreate command (used for starting a giveaway) is in src/commands/gCreate.ts. Open this file to view and modify the command's logic.

Step 2: Modify the Command Logic

Commands are typically structured like this:

import {
    SlashCommandBuilder,
} from "discord.js";
import { SlashCommand } from "../types";
import GuildManager from "../database/guildManager";
import GiveawayManager from "../database/gwManager";

const command: SlashCommand = {
    command: new SlashCommandBuilder()
        .setName("gend")
        .setDescription("End a giveaway early")
        .addStringOption((option) =>
            option
                .setName("messageid")
                .setDescription("The message ID of the giveaway")
                .setRequired(true)
        )
        .addBooleanOption((option) =>
            option
                .setName("selectwinners")
                .setDescription("Whether to select winners or not")
                .setRequired(false)
        ),
    execute: async (interaction) => {
        const giveawayId = interaction.options.getString("messageid");
        const selectWinners =
            interaction.options.getBoolean("selectwinners") || false;

        const gw = new GiveawayManager();
        const guild = new GuildManager();

        const giveaway = gw.fetchGiveaway(giveawayId!);

        if (!giveaway) {
            return interaction.reply("Invalid giveaway ID");
        }

        // Your custom giveaway ending logic

        await interaction.reply("Giveaway ended!");

    },
};

export default command;

To customize:

  1. Modify the command description or options.
  2. Update the logic inside the execute function to change how the command behaves.

Example: Adding a Winner Count Option

To add an option for specifying the number of winners:

  1. Add the new option to the command:
.addNumberOption((option) =>
  option.setName("winners").setDescription("Number of winners").setRequired(false)
);
  1. Update the logic in the execute function:
const winners = interaction.options.getNumber("winners") || 1; // Default to 1 winner
await interaction.reply(
  `🎉 Giveaway for **${prize}** started! Duration: ${duration} minutes, Winners: ${winners}.`
);

Creating New Commands

Adding new commands to the bot is easy! Follow these steps:

Step 1: Create a New Command File

  1. Go to the src/commands folder.
  2. Create a new file with a descriptive name, such as gStatus.ts.

Step 2: Define the Command Structure

Use the following template for a new command:

import {
    SlashCommandBuilder,
} from "discord.js";
import { SlashCommand } from "../types";

const command: SlashCommand = {
    command: new SlashCommandBuilder()
        .setName("gstatus")
        .setDescription("Check the status of currently running giveaways")
    execute: async (interaction) => {
        // Your Logic Here

        await interaction.reply("Here are the currently active giveaways...");
    }
}

export default command;

Step 3: Implement the Logic

For example, if you want to list active giveaways:

import GiveawayManager from "../database/gwManager";
const gwManager = new GiveawayManager(); // You must make a new instance of the class

export async function execute(interaction: CommandInteraction) {
  const giveaways = await gwManager.getActiveGiveaways(); // Custom function in `gwManager.ts`
  
  if (giveaways.length === 0) {
    return interaction.reply("No active giveaways found.");
  }

  const giveawayList = giveaways
    .map((gw) => `🎁 **${gw.prize}** - Ends in ${gw.duration} minutes.`)
    .join("\n");

  await interaction.reply(`Active Giveaways:\n${giveawayList}`);
}

Step 4: Register the Command

The command will be automatically registered using the command handler!

Extending Functionality

To extend the bot's capabilities, you can customize database logic, add utilities, or modify existing events.

Extending Database Logic

  • Location: src/database
  • Example: Add a new function in gwManager.ts to fetch giveaways by guild:
export async function getGiveawaysByGuild(guildId: string) {
  return this.selectAll("SELECT * FROM giveaways WHERE guildId = ?").all(guildId);
}
  • Use this new function in a command or event to add guild-specific features.

Adding Utilities

  • Location: src/functions.ts
  • Example: Create a utility to format timestamps:
export function formatTime(timestamp: number): string {
  const date = new Date(timestamp);
  return date.toLocaleString();
}
  • Use this utility in commands or database queries.

Enhancing Events

  • Location: src/events
  • Example: Add a new event in guildCreate.ts to initialize guild settings:
import { Guild } from "discord.js";
import { BotEvent } from "../types";
import { color } from "../functions";
import GuildManager from "../database/guildManager";

const database = new GuildManager();

const event: BotEvent = {
    name: "guildCreate",
    execute: (guild: Guild) => {
        database.createGuild(guild.id);
        console.log(
            color(
                "text",
                `🌠 Successfully created guild ${color("variable", guild.name)}`
            )
        );
    },
};

export default event;

Handlers in the Bot

Handlers in the bot are responsible for organizing and automating the process of loading commands and events. By using handlers, you ensure that the bot dynamically reads all commands and events from their respective directories, making it easy to add new functionality without manually modifying the core bot file.

Command Handler

The command handler is responsible for registering and managing all the commands in the bot. It dynamically loads all command files from the 📂src/commands directory and registers them with Discord.

How It Works

Here’s an example implementation of the command handler:

import { Client, Routes, SlashCommandOptionsOnlyBuilder } from "discord.js";
import { REST } from "@discordjs/rest";
import { readdirSync } from "fs";
import { join } from "path";
import { color } from "../functions";
import { SlashCommand } from "../types";

module.exports = (client: Client) => {
    const slashCommands: SlashCommandOptionsOnlyBuilder[] = [];

    let slashCommandsDir = join(__dirname, "../commands");

    // Dynamically read all files in the commands folder
    readdirSync(slashCommandsDir).forEach((file) => {
        if (!file.endsWith(".js")) return; // Ensure only JS files are loaded
        let command: SlashCommand =
            require(`${slashCommandsDir}/${file}`).default;

        // Add the command to the slashCommands array for registration
        slashCommands.push(command.command);

        // Map the command to the client's collection for later use
        client.slashCommands.set(command.command.name, command);
    });

    // Register the commands with Discord using the REST API
    const rest = new REST({ version: "10" }).setToken(process.env.TOKEN);

    rest.put(Routes.applicationCommands(process.env.CLIENT_ID), {
        body: slashCommands.map((command) => command.toJSON()),
    })
        .then((data: any) => {
            console.log(
                color(
                    "text",
                    `🔥 Successfully loaded ${color(
                        "variable",
                        data.length
                    )} slash command(s)`
                )
            );
        })
        .catch((e) => {
            console.log(e);
        });
};

Adding a New handler

If you want a function to be ran on bot load, you can create it as a new handler.

  1. Create a file Logging.ts in the 📂src/handlers folder.
import { Client } from "discord.js";
import { readdirSync } from "fs";
import { join } from "path";
import { color } from "../functions";
import { BotEvent } from "../types";

module.exports = (client: Client) => {
    let eventsDir = join(__dirname, "../events/logging");
    let num = 0;
    readdirSync(eventsDir).forEach((file) => {
        if (!file.endsWith(".js")) return;
        let event: BotEvent = require(`${eventsDir}/${file}`).default;
        event.once
            ? client.once(event.name, (...args) => event.execute(...args))
            : client.on(event.name, (...args) => event.execute(...args));
        num++;
    });
    console.log(
        color(
            "text",
            `🌠 Successfully loaded ${color("variable", num)} logging event(s)`
        )
    );
};
  1. The event handler will automatically load any logging events in the 📂src/events/logging folder and register them.

  2. You can now create new logging events in the 📂src/events/logging folder and they will be automatically loaded by the handler

You can use handlers for more complicated functions but this is a good starting point.

Registering Commands with Discord

After editing or adding commands, don’t forget to re-load your discord bot to view the new commands:

npm run build
npm run start

This updates the bot’s commands on Discord.

Testing Your Changes

Once satisfied, build and start the bot in production mode:

npm run build
npm start

By following this guide, you can easily customize the Giveaway Bot template to meet your needs, add new features, or improve existing ones. Have fun coding! 🎉