1.6.0 • Published 9 months ago

horizon-handler v1.6.0

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

Horizon Handler

A powerful Discord bot commands, events, and components handler, fully written in TypeScript and has many typings features. Horizon Handler provides simple Discord application commands handler, supports custom options and custom arguments for listeners.

Important This package is not a part of discord.js and it's completely a separate 3ʳᵈ party package.

Features

  • Open-source project.
  • Supports all type of application commands from Discord API.
  • Three available handlers:
    • Commands: Handles Discord API application commands and it's interactions.
      • Chat Input commands.
      • User context menu commands.
      • Message context menu commands.
    • Events: Handles Gateway events from discord.js Client's events.
    • Components: Handles components by their custom ID.
      • Select menus (any type).
      • Buttons.
      • Modal submits.
  • Built-in collections (Collection from discord.js).
  • Autocomplete interactions supported.
  • 99.9% Promise-based.
  • Fully written in TypeScript.
  • CLI commands.
  • Easy and simple to use.

Table of Contents

Install

Required platforms

  • Node.js v16.9.0 or newer (recommended: v18 LTS)

Required packages

Warning If you're using TypeScript, you must install the package typescript v5.1.6 or newer.

After you meet all the requirements, you can install the package.

npm install horizon-handler
yarn add horizon-handler
pnpm add horizon-handler

↑ Table of Contents

Links

Documentation: Click here Source (GitHub): Click here Issues: Click here Pull requests: Click here

↑ Table of Contents

Example Usage

This is an example usage written in TypeScript. To skip this, use the CLI commands: Click here

Tree of the project

Example Bot
├─── src
│    ├─── index.ts
│    ├─── events
│    │   └─── ready.ts
│    │   └─── interactionCreate.ts
│    └─── commands
│         └─── Utility
│              └─── ping.ts
├─── package.json
└─── tsconfig.json

tsconfig.json compiler options

Note For this example, the out directory name is dist. You can change it at anytime, but make sure that the path directory name from CommandsHandler, EventsHandler, and/or ComponentsHandler constructor parameter are also renamed to the new one.

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "CommonJS",
        "outDir": "dist",
        "strict": true
    },
    "include": [
        "src"
    ],
    "exclude": [
        "node_modules",
        "dist"
    ]
}

Create a new Discord bot client: (index.ts)

import { Client } from 'discord.js';

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

client.login('Your bot token goes here');

Define a new commands & events handler and load all the files: (index.ts)

import { CommandsHandler, Events, EventsHandler } from 'horizon-handler';

export const commandshandler = new CommandsHandler<Client>('./dist/commands/', true);

commandshandler.on(Events.FileLoad, (command) => console.log(`Loaded new command: ` + command.name));

export const eventshandler = new EventsHandler<Client>('./dist/events/');

eventshandler.on(Events.FileLoad, (event) => console.log(`Loaded new event: ` + event));

(async () => {
    await commandshandler.load();

    await eventshandler.load(client);
})();

Create a new simple command: (ping.ts)

import { SlashCommandBuilder } from 'discord.js';
import { CommandType } from 'horizon-handler';
import { commandshandler } from '../../index';

export default new commandshandler.command({
    type: CommandType.ChatInput,
    structure: new SlashCommandBuilder()
        .setName('ping')
        .setDescription('Replies with Pong!'),
    run: async (client, interaction) => {
        await interaction.reply({
            content: 'Pong!'
        });
    }
});

Create a new event to log whenever the client is ready or not and deploy the application commands to Discord API: (ready.ts)

import { eventshandler, commandshandler } from '../index';

export default new eventshandler.event({
    event: 'ready',
    once: true,
    run: async (_, client) => {
        console.log('Logged in as: ' + client.user.displayName);

        await commandshandler.deploy(client);
    }
});

Create a new event to handle application commands: (interactionCreate.ts)

import { eventshandler, commandshandler } from '../index';

export default new eventshandler.event({
    event: 'interactionCreate',
    run: (client, interaction) => {
        if (!interaction.isChatInputCommand()) return;

        const command = commandshandler.collection.get(interaction.commandName);

        if (!command || command.type !== 1) return;

        try {
            command.run(client, interaction);
        } catch (e) {
            console.error(e);
        };
    }
});

↑ Table of Contents

CLI commands

npx horizon-handler

How to use:

hhandler [command] [options] <path>
CommandArgumentsOptionsDescription
js-examplepath: string--token: stringCreate a new project for a Discord bot, written in JavaScript.
ts-examplepath: string--token: stringCreate a new project for a Discord bot, written in TypeScript.
links--View all possible useful and informative links of this package.
about--About Horizon Handler.

To set your bot token in the JavaScript or TypeScript example, use the option --token with the argument with type of string.

hhandler [ts-example/js-example] <path> --token 'Your bot token'

How to fix the error "MODULE_NOT_FOUND"?

This package uses three libraries for the CLI commands: commander, fs-extra, and colors. Install them globally:

npm install -g commander fs-extra colors

↑ Table of Contents

Other Examples

Using custom options for commands:

By default, the handler will make all properties in O (Type parameter for custom options) optional.

import { Client } from 'discord.js';

interface Options {
    option1: string,
    option2: number,
    option3: (string | number)[],
    option4: (...args: any[]) => void,
    option5: { suboption: string }
};

new CommandsHandler<Client, Options>(...);

↑ Table of Contents

Custom events for Events handler:

Warning Make sure to emit these events from Client using the method emit(...) so they will be able to listen.

import { Client, ClientEvents } from 'discord.js';

type CustomEvents = {
    a: [x: string, y: number],
    b: [z: { }, w: any, string],
    c: [string, number, any, { }, void, unknown, []]
};

new EventsHandler<Client, keyof ClientEvents, CustomEvents>(...);

export default new [handler].customevent(...);

↑ Table of Contents

Custom arguments for Commands handler:

import { Client } from 'discord.js';

type Args = [
    x: string,
    y: number,
    z?: any // ← Optional because of the question mark (?)
];

new CommandsHandler<Client, { }, Args>(...);

↑ Table of Contents

Handle Autocomplete interactions:

Note This example is continued with the Example usage: Click here

Create a new interactionCreate event file for autocomplete interactions:

import { eventshandler, collection } from "../index";

export default new eventshandler.event({
    event: 'interactionCreate',
    run: async (client, interaction) => {
        if (!interaction.isAutocomplete()) return;

        const command = collection.get(interaction.commandName);

        if (!command || command.type !== 1) return;

        try {
            if (command.autocomplete) command.autocomplete(client, interaction);
        } catch (e) {
            console.error(e);
        };
    }
});

Create a command example with autocomplete option:

import { SlashCommandBuilder } from 'discord.js';
import { CommandType } from 'horizon-handler';
import { cmdshandler } from '../../index';

export default new cmdshandler.command({
    type: CommandType.ChatInput,
    structure: new SlashCommandBuilder()
        .setName('autocomplete-command')
        .setDescription('An autocomplete interaction command!')
        .addStringOption((opt) =>
            opt.setName('guild')
                .setDescription('Choose a server.')
                .setAutocomplete(true)
                .setRequired(true)
        ),
    run: async (client, interaction) => {
        const guild = interaction.options.getString('guild', true);

        await interaction.reply({
            content: 'You choosed: ' + guild
        });
    },
    autocomplete: async (client, interaction) => {
        const focused = interaction.options.getFocused();
        
        const guilds = client.guilds.cache.map((guild) => guild.name);
        
        const filtered = guilds.filter((choice) => choice.startsWith(focused));

        await interaction.respond(
			filtered.map(
                (choice) => ({ name: choice, value: choice })
            )
	    );
    }
});

↑ Table of Contents

Add components or commands without creating a file:

Note The difference between add and set that the first one (add) will set more keys in the collection, even overwrites the old key's data. The other one (set) will clear the entire collection and then adds the commands/components in the collection.

Warning Even if you're not providing files for the handler and using these methods, you must at least create one file that actually exist with it's valid directory path.

[commands handler].addCommands(
    {
        type: ...,
        structure: ...,
        run: (...) => ...
    },
    ...
);

[commands handler].setCommands(
    {
        type: ...,
        structure: ...,
        run: (...) => ...
    },
    ...
);

[components handler].addComponents(
    {
        type: ...,
        customId: ...,
        run: (...) => ...
    },
    ...
);

[components handler].setComponents(
    {
        type: ...,
        customId: ...,
        run: (...) => ...
    },
    ...
);

↑ Table of Contents

Support

Need any help? Join our Discord server, report to us the problem, and we will solve it for you!

↑ Table of Contents

License

The MIT License. (View here)

↑ Table of Contents

1.5.4

9 months ago

1.6.0

9 months ago

1.6.0-pre-1

9 months ago

1.6.0-pre-2

9 months ago

1.5.3

9 months ago

1.5.2

9 months ago

1.5.1

9 months ago

1.5.0

9 months ago

1.4.4

9 months ago

1.4.3

9 months ago

1.4.2

9 months ago

1.4.1

9 months ago

1.4.0

9 months ago

1.3.1

9 months ago

1.3.0

9 months ago

1.3.0-pre-1

9 months ago

1.2.2

9 months ago

1.2.1

9 months ago

1.2.0

9 months ago

1.1.0

9 months ago

1.0.3

9 months ago

1.0.2

9 months ago

1.0.1

9 months ago

1.0.0

9 months ago