diff --git a/src/commands/general/node.ts b/src/commands/general/node.ts index ee94662..0d8de48 100644 --- a/src/commands/general/node.ts +++ b/src/commands/general/node.ts @@ -4,7 +4,7 @@ import { RandomLoadingMessage } from "../../lib/constants"; import { pickRandom } from "../../lib/utils"; import { Node } from "sakulink"; import { stripIndent } from "common-tags"; -import { Message } from "discord.js"; +import { Message, OmitPartialGroupDMChannel } from "discord.js"; import ms from "ms"; /** @@ -15,6 +15,7 @@ import ms from "ms"; @ApplyOptions({ // The description of the command. description: "Display nodes information", + aliases: ["nodes"], }) export class UserCommand extends Command { /** @@ -37,8 +38,38 @@ export class UserCommand extends Command { * @returns The response to send to the user. * @since 1.0.0 */ - public override async chatInputRun(interaction: Command.ChatInputCommandInteraction): Promise> { - await interaction.reply({ content: pickRandom(RandomLoadingMessage) }); + public override async chatInputRun( + interaction: Command.ChatInputCommandInteraction, + ): Promise | OmitPartialGroupDMChannel> | undefined> { + return await this.run(interaction); + } + + /** + * Runs the command when it is invoked. + * + * @param message The message that invoked the command. + * @returns The response to send to the user. + * @since 1.0.0 + */ + public override async messageRun(message: Message) { + return await this.run(message); + } + + /** + * Runs the command when it is invoked. + * + * @param interactionOrMessage The message that invoked the command. + * @returns The response to send to the user. + * @since 1.0.0 + */ + private async run( + interactionOrMessage: Command.ChatInputCommandInteraction | Message, + ): Promise | OmitPartialGroupDMChannel> | undefined> { + const isMessage = interactionOrMessage instanceof Message; + let message: Message | null = null; + + if (isMessage) message = await (interactionOrMessage).reply({ content: pickRandom(RandomLoadingMessage) }); + else await interactionOrMessage.reply({ content: pickRandom(RandomLoadingMessage) }); // Get the list of nodes from the manager const nodes = this.container.client.manager.nodes.map((node: Node) => ({ @@ -72,6 +103,7 @@ export class UserCommand extends Command { }; // Edit the previous message with the embed - return await interaction.editReply({ embeds: [embed], content: null }); + if (isMessage) return await message?.edit({ embeds: [embed], content: null }); + return await interactionOrMessage.editReply({ embeds: [embed], content: null }); } } diff --git a/src/commands/general/ping.ts b/src/commands/general/ping.ts index 384fd75..9f7aec5 100644 --- a/src/commands/general/ping.ts +++ b/src/commands/general/ping.ts @@ -1,6 +1,6 @@ import { ApplyOptions } from "@sapphire/decorators"; import { Command } from "@sapphire/framework"; -import { InteractionResponse } from "discord.js"; +import { InteractionResponse, Message } from "discord.js"; /** * A command that pings the bot and returns the latency. @@ -31,9 +31,24 @@ export class UserCommand extends Command { * @returns The response to send to the user. * @since 1.0.0 */ - public override async chatInputRun(interaction: Command.ChatInputCommandInteraction): Promise> { + public override async chatInputRun(interaction: Command.ChatInputCommandInteraction): Promise | Message> { + return await this.run(interaction); + } + + /** + * Runs the command when it is invoked. + * + * @param message The message that invoked the command. + * @returns The response to send to the user. + * @since 1.0.0 + */ + public override async messageRun(message: Message) { + return await this.run(message); + } + + private async run(interactionOrMessage: Command.ChatInputCommandInteraction | Message) { const content = `Pong! ${Math.round(this.container.client.ws.ping)}ms`; // The content of the response. - return interaction.reply({ content }); + return await interactionOrMessage.reply({ content }); } } diff --git a/src/config.ts b/src/config.ts index a2f55c8..1973394 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,7 @@ import { NodeOptions, SearchPlatform } from "sakulink"; export const token: string = ""; +export const prefix: string = "*"; export const defaultSearchPlatform: SearchPlatform = "youtube music"; export const defaultVolume: number = 75; export const nodes: NodeOptions[] = [ diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts b/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts index fe9f285..13bed3a 100644 --- a/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts +++ b/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts @@ -3,7 +3,7 @@ import { Listener, UserError } from "@sapphire/framework"; import { InteractionResponse, Message } from "discord.js"; /** - * {@link ChatInputCommandDenied} event + * The "chatInputCommandDenied" event listener class. * * @since 1.0.0 */ diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts b/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts index caf959a..ac383a5 100644 --- a/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts +++ b/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts @@ -4,7 +4,7 @@ import { ClientEvents } from "discord.js"; import { Logger } from "@sapphire/plugin-logger"; /** - * {@link ChatInputCommandSuccess} event + * The "chatInputCommandSuccess" event listener class. * * @since 1.0.0 */ diff --git a/src/listeners/commands/messageCommands/messageCommandDenied.ts b/src/listeners/commands/messageCommands/messageCommandDenied.ts new file mode 100644 index 0000000..c027b72 --- /dev/null +++ b/src/listeners/commands/messageCommands/messageCommandDenied.ts @@ -0,0 +1,25 @@ +import { Events, MessageCommandDeniedPayload } from "@sapphire/framework"; +import { Listener, UserError } from "@sapphire/framework"; + +/** + * The "messageCommandDenied" event listener class. + * + * @since 1.0.0 + */ +export class UserEvent extends Listener { + /** + * Runs once the event is fired + * + * @param context The context that contains the user error + * @param message The message that contains the payload + * @since 1.0.0 + */ + public override async run({ context, message: content }: UserError, { message }: MessageCommandDeniedPayload) { + // If the error was marked as silent, do not reply + if (Reflect.get(Object(context), "silent")) return; + + // Reply to the message with the user error + return message.reply({ content, allowedMentions: { users: [message.author.id], roles: [] } }); + } +} + diff --git a/src/listeners/commands/messageCommands/messageCommandSuccess.ts b/src/listeners/commands/messageCommands/messageCommandSuccess.ts new file mode 100644 index 0000000..a5f1a0a --- /dev/null +++ b/src/listeners/commands/messageCommands/messageCommandSuccess.ts @@ -0,0 +1,30 @@ +import { MessageCommandSuccessPayload } from "@sapphire/framework"; +import { Listener, LogLevel } from "@sapphire/framework"; +import { Logger } from "@sapphire/plugin-logger"; +import { logSuccessCommand } from "../../../lib/utils"; + +/** + * The "messageCommandSuccess" event listener class. + * + * @since 1.0.0 + */ +export class UserEvent extends Listener { + /** + * Runs once the event is fired. + * + * @param payload - The payload of the event. + * @since 1.0.0 + */ + public override run(payload: MessageCommandSuccessPayload): void { + logSuccessCommand(payload); + } + + /** + * Runs when the listener is being loaded, it enables the listener only if the logger is set to debug level + * @since 1.0.0 + */ + public override onLoad(): unknown { + this.enabled = (this.container.logger).level <= LogLevel.Debug; + return super.onLoad(); + } +} diff --git a/src/structures/Client.ts b/src/structures/Client.ts index 0af676d..4f91178 100644 --- a/src/structures/Client.ts +++ b/src/structures/Client.ts @@ -1,5 +1,5 @@ import { LogLevel, SapphireClient } from "@sapphire/framework"; -import { token } from "../config"; +import { prefix, token } from "../config"; import { Lavalink } from "./Lavalink"; /** @@ -28,8 +28,24 @@ export class Client extends SapphireClient { * @remarks * We use the `GUILDS` and `GUILD_VOICE_STATES` intents because we need to listen to guild events * and to access the voice states of users. + * We use the `GUILD_MESSAGES` and `MESSAGE_CONTENT` intents because we need to listen to message events. */ - intents: 129, // GUILDS, GUILD_VOICE_STATES + intents: 33409, // GUILDS, GUILD_VOICE_STATES, GUILD_MESSAGES, MESSAGE_CONTENT + /** + * The default prefix for the client. + */ + defaultPrefix: prefix, + /** + * Whether or not to load message command listeners when the client starts. + * This is set to `true` by default, so message command listeners will be loaded when the client starts. + */ + loadMessageCommandListeners: true, + + /** + * Whether or not to make command matching case-insensitive. + * This is set to `true` by default, so command matching will be case-insensitive. + */ + caseInsensitiveCommands: true, }); }