From d399e56c7dabf02120279a13bb951b3ffde4ef99 Mon Sep 17 00:00:00 2001 From: Bryce Pollack <87786776+brycepollack@users.noreply.github.com> Date: Sat, 13 Jun 2026 22:36:34 -0700 Subject: [PATCH 1/2] init --- CHANGELOG.md | 2 ++ src/bot.ts | 2 ++ src/commands/help.ts | 25 +++++++++++++++++++++++++ src/commands/index.ts | 1 + src/inversify.config.ts | 2 ++ 5 files changed, 32 insertions(+) create mode 100644 src/commands/help.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a15570ad4..74d0f148a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Add /help command to list all available commands. + ## [2.11.5] - 2026-06-04 - Fix queue-empty crashes when auto-announce is enabled or playback ends without a next song. diff --git a/src/bot.ts b/src/bot.ts index 949d9f024..58399f7ec 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -53,6 +53,8 @@ export default class { } } + this.commandsByName.get('help')?.setCommands?.([...this.commandsByName.values()]); + // Register event handlers // eslint-disable-next-line complexity this.client.on('interactionCreate', async interaction => { diff --git a/src/commands/help.ts b/src/commands/help.ts new file mode 100644 index 000000000..e180a00da --- /dev/null +++ b/src/commands/help.ts @@ -0,0 +1,25 @@ +import {ChatInputCommandInteraction} from 'discord.js'; +import {injectable} from 'inversify'; +import Command from './index.js'; +import {SlashCommandBuilder} from '@discordjs/builders'; + +@injectable() +export default class implements Command { + public readonly slashCommand = new SlashCommandBuilder() + .setName('help') + .setDescription('shows all available commands'); + + private commands: Command[] = []; + + public setCommands(commands: Command[]) { + this.commands = commands; + } + + public async execute(interaction: ChatInputCommandInteraction): Promise { + const commandList = this.commands + .map(c => `**/${c.slashCommand.name}** — ${c.slashCommand.description}`) + .join('\n'); + + await interaction.reply({content: commandList, ephemeral: true}); + } +} diff --git a/src/commands/index.ts b/src/commands/index.ts index 60cf6ced2..ed37af517 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -8,4 +8,5 @@ export default interface Command { execute: (interaction: ChatInputCommandInteraction) => Promise; handleButtonInteraction?: (interaction: ButtonInteraction) => Promise; handleAutocompleteInteraction?: (interaction: AutocompleteInteraction) => Promise; + setCommands?: (commands: Command[]) => void; } diff --git a/src/inversify.config.ts b/src/inversify.config.ts index 8e621cbce..1405dee4c 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -38,6 +38,7 @@ import Skip from './commands/skip.js'; import Stop from './commands/stop.js'; import Unskip from './commands/unskip.js'; import Volume from './commands/volume.js'; +import Help from './commands/help.js'; import ThirdParty from './services/third-party.js'; import FileCacheProvider from './services/file-cache.js'; import KeyValueCacheProvider from './services/key-value-cache.js'; @@ -96,6 +97,7 @@ if (config.SPOTIFY_CLIENT_ID !== '' && config.SPOTIFY_CLIENT_SECRET !== '') { Stop, Unskip, Volume, + Help ].forEach(command => { container.bind(TYPES.Command).to(command).inSingletonScope(); }); From 9be574c0dab3db8ec6e1fa27dff46933b0bcc67c Mon Sep 17 00:00:00 2001 From: Bryce Pollack <87786776+brycepollack@users.noreply.github.com> Date: Sun, 14 Jun 2026 10:01:43 -0700 Subject: [PATCH 2/2] fix lint errors --- src/commands/help.ts | 2 +- src/inversify.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/help.ts b/src/commands/help.ts index e180a00da..d8ac4dfa1 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -17,7 +17,7 @@ export default class implements Command { public async execute(interaction: ChatInputCommandInteraction): Promise { const commandList = this.commands - .map(c => `**/${c.slashCommand.name}** — ${c.slashCommand.description}`) + .map(c => `**/${c.slashCommand.name ?? ''}** — ${c.slashCommand.description ?? ''}`) .join('\n'); await interaction.reply({content: commandList, ephemeral: true}); diff --git a/src/inversify.config.ts b/src/inversify.config.ts index 1405dee4c..0aaeae407 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -97,7 +97,7 @@ if (config.SPOTIFY_CLIENT_ID !== '' && config.SPOTIFY_CLIENT_SECRET !== '') { Stop, Unskip, Volume, - Help + Help, ].forEach(command => { container.bind(TYPES.Command).to(command).inSingletonScope(); });