Decorators

Decorators can be used to easily add functionality to your modules. By default decorators are created with the typescript template of create-reciple. To use decorators, you need to install the @reciple/decorators package in your project and set experimentalDecorators to true in your tsconfig.json.

If you don’t have the @reciple/decorators package installed, you can install it with npm i @reciple/decorators.

npm i @reciple/decorators

tsconfig.json

{
    "compilerOptions": {
        // ...Other compiler options...
        "experimentalDecorators": true
    }
}

Writing Modules with Decorators

To use decorators, you need to import the @reciple/decorators package in your module. Then add the @setRecipleModule() decorator to your module class. This decorator will set your module’s metadata to the prototype using the recipleModuleMetadataSymbol symbol property.

import { setRecipleModule } from '@reciple/decorators';

@setRecipleModule()
export class PingCommand implements RecipleModuleData {
    public async onStart(): Promise<boolean> {
        return true;
    }
}

export default new PingCommand();

@setRecipleModule() takes an optional versions parameter. This parameter will set the versions property of your module.

import { setRecipleModule } from '@reciple/decorators';

@setRecipleModule('^9.0.0') // or @setRecipleModule(['^9.0.0'])
export class PingCommand implements RecipleModuleData {
    public async onStart(): Promise<boolean> {
        return true;
    }
}

export default new PingCommand();

similar to

export class PingCommand implements RecipleModuleData {
    public versions = '^9.0.0';

    public async onStart(): Promise<boolean> {
        return true;
    }
}

export default new PingCommand();

You can also pass an object to @setRecipleModule() to set the module’s Id and name.

import { setRecipleModule } from '@reciple/decorators';

@setRecipleModule({ id: 'ping', name: 'Ping', versions: '^9.0.0' })
export class PingCommand implements RecipleModuleData {
    public async onStart(): Promise<boolean> {
        return true;
    }
}

export default new PingCommand();

Adding Commands With Decorators

To add commands with decorators, adding @setRecipleModuleStart() decorator is required to set the created commands from the class’ prototype metadata to the module’s .commands property.

Context Menu Commands

@setContextMenuCommand() decorator is used to add context menu commands to your module. First parameter of the decorator takes a command object or builder.

import { setContextMenuCommand, setRecipleModule, setRecipleModuleStart } from '@reciple/decorators';
import { ContextMenuCommandExecuteData, RecipleModuleData } from 'reciple';
import { ApplicationCommandType } from 'discord.js';

@setRecipleModule()
export class PingCommand implements RecipleModuleData {
    @setRecipleModuleStart()
    public async onStart(): Promise<boolean> {
        return true;
    }

    @setContextMenuCommand({ name: 'Ping', type: ApplicationCommandType.Message })
    public async handlePingCommand({ interaction }: ContextMenuCommandExecuteData): Promise<void> {
        await interaction.reply('Pong!');
    }
}

export default new PingCommand();
Pong!

Message Commands

@setMessageCommand() decorator is used to add message commands to your module. First parameter of the decorator takes a command object or builder.

import { setMessageCommand, setRecipleModule, setRecipleModuleStart } from '@reciple/decorators';
import { MessageCommandExecuteData, RecipleModuleData } from 'reciple';
import { ApplicationCommandType } from 'discord.js';

@setRecipleModule()
export class PingCommand implements RecipleModuleData {
    @setRecipleModuleStart()
    public async onStart(): Promise<boolean> {
        return true;
    }

    @setMessageCommand({ name: 'ping', description: 'Ping command' })
    public async handlePingCommand({ message }: MessageCommandExecuteData): Promise<void> {
        await message.reply('Pong!');
    }
}

export default new PingCommand();
!ping !ping Pong!

Slash Commands

@setSlashCommand() decorator is used to add slash commands to your module. First parameter of the decorator takes a command object or builder.

import { setSlashCommand, setRecipleModule, setRecipleModuleStart } from '@reciple/decorators';
import { SlashCommandExecuteData, RecipleModuleData } from 'reciple';
import { ApplicationCommandType } from 'discord.js';

@setRecipleModule()
export class PingCommand implements RecipleModuleData {
    @setRecipleModuleStart()
    public async onStart(): Promise<boolean> {
        return true;
    }

    @setSlashCommand({ name: 'ping', description: 'Ping command' })
    public async handlePingCommand({ interaction }: SlashCommandExecuteData): Promise<void> {
        await interaction.reply('Pong!');
    }
}

export default new PingCommand();
Pong!

Stacking Command Decorators

import { setContextMenuCommand, setMessageCommand, setRecipleModule, setRecipleModuleLoad, setRecipleModuleStart, setRecipleModuleUnload, setSlashCommand } from '@reciple/decorators';
import { AnyCommandExecuteData, CommandType, RecipleModuleData } from "reciple";
import { ApplicationCommandType, type Message } from 'discord.js';

@setRecipleModule()
export class PingCommand implements RecipleModuleData {
    @setRecipleModuleStart()
    public async onStart(): Promise<boolean> {
        return true;
    }

    @setContextMenuCommand({ name: 'Ping', type: ApplicationCommandType.Message })
    @setMessageCommand({ name: 'ping', description: 'Ping command' })
    @setSlashCommand({ name: 'ping', description: 'Ping command' })
    async handleCommandExecute(data: AnyCommandExecuteData): Promise<void> {
        switch (data.type) {
            case CommandType.ContextMenuCommand:
            case CommandType.SlashCommand:
                await data.interaction.reply('Pong!');
                return;
            case CommandType.MessageCommand:
                await data.message.reply('Pong!');
                return;
        }
    }
}

export default new PingCommand();
Pong! !ping !ping Pong! Pong!

Adding Event Listeners With Decorators

@reciple/decorators provides:

These decorators are used to set a class method into an event listener. These decorators takes the event name as the first argument. You can set the event as once by setting the second argument to true.

Event Register

These event listeners are registered to its event emitter after the module is loaded (the bot is logged in), it means that it’s required to use @setRecipleModuleLoad() decorator to register the event listeners.

You can make the @setRecipleModuleStart()’s first parameter as true to register the event listeners even before the module is loaded.

INFO
Although not required, it is recommended to use @setRecipleModuleUnload() decorator on your onUnload() method to unregister the event listeners when the module is unloaded.
import { setClientEvent, setRecipleModule, setRecipleModuleStart } from '@reciple/decorators';
import { RecipleModuleData } from 'reciple';
import { ApplicationCommandType } from 'discord.js';

@setRecipleModule()
export class MentionEvent implements RecipleModuleData {
    @setRecipleModuleStart()
    public async onStart(): Promise<boolean> {
        return true;
    }

    @setRecipleModuleLoad()
    public async onLoad(): Promise<void> {}

    @setRecipleModuleUnload()
    public async onUnload(): Promise<void> {}

    @setClientEvent('messageCreate') // or @setClientEvent('messageCreate', true) for once event
    public async onMessageCreate(message: Message): Promise<void> {
        const bot = message.client.user?.id;
        if (!bot || !message.content.include(bot)) return;

        await message.reply('You mentioned me!');
    }
}

export default new MentionEvent();
963047284772306976 963047284772306976 You mentioned me!

Interaction Listeners

If you have reciple-interaction-events installed, you can use its provided decorators to register an event listener to an interaction.

IMPORTANT
It is required to add @setRegisterInteractionEvents() method decorator to your onStart or onLoad method to register the event listeners.
import { InteractionListenerType, setRegisterInteractionEvents, setInteractionEvent } from 'reciple-interaction-events';
import { setRecipleModule, setRecipleModuleStart, setMessageCommand } from '@reciple/decorators';
import { ButtonBuilder, ButtonStyle, ComponentType, ButtonInteraction } from 'discord.js';
import type { MessageCommandExecuteData, RecipleModuleData } from 'reciple';

@setRecipleModule()
export class MyModule implements RecipleModuleData {
    @setRecipleModuleStart()
    @setRegisterInteractionEvents()
    async onStart(): Promise<boolean> {
        return true;
    }

    @setMessageCommand({ name: 'test', description: 'Go test' })
    async handleMessageCommand({ message }: MessageCommandExecuteData): Promise<void> {
        await message.reply({
            content: `Hello, world ${message.author}!`,
            components: [
                {
                    type: ComponentType.ActionRow,
                    components: [
                        new ButtonBuilder()
                            .setLabel('Delete Message')
                            .setCustomId('delete-message')
                            .setStyle(ButtonStyle.Secondary)
                    ]
                }
            ]
        });
    }

    @setInteractionEvent({ type: InteractionListenerType.Button, customId: 'delete-message' })
    async onInteraction(interaction: ButtonInteraction): Promise<void> {
        await interaction.deferUpdate();
        await interaction.message.delete();
    }
}

export default new MyModule();