QOL Updates

Global help flag and corresponding printout
Added descriptions to commands
Added command to list commands
This commit is contained in:
Erik 2024-03-28 21:49:18 +02:00
parent 5685f0a85e
commit 5ae86b5785
18 changed files with 107 additions and 18 deletions

View File

@ -36,7 +36,8 @@ export type CommandDefinition = {
help?: string,
limited?: Snowflake[],
showUsage?: boolean,
sameVc?: boolean
sameVc?: boolean,
description?: string,
} & Omit<ComponentOptions, 'type'>
export type ObserverOptions = {

View File

@ -20,7 +20,7 @@
"dependencies": {
"@discordjs/opus": "^0.9.0",
"@discordjs/voice": "^0.16.1",
"@navy.gif/commandparser": "^1.6.7",
"@navy.gif/commandparser": "^1.6.8",
"@navy.gif/logger": "^2.5.4",
"@navy.gif/timestring": "^6.0.6",
"@types/node": "^20.11.30",

View File

@ -98,7 +98,8 @@ class DiscordClient extends Client
this.#built = true;
this.emit('built');
this.#musicPlayer.initialise();
if (!process.env.DISABLE_MUSIC)
this.#musicPlayer.initialise();
return this;
}

View File

@ -240,6 +240,7 @@ class MusicLibrary implements Initialisable
return this.#logger.info('No index file found');
const raw = fs.readFileSync(indexPath, { encoding: 'utf-8' });
const parsed = JSON.parse(raw) as MusicIndexEntry[];
this.#index.clear();
for (const entry of parsed)
{
if (entry.id >= this.#currentId)

View File

@ -74,7 +74,7 @@ class Registry
throw new Error('Attempted to load an invalid component.');
if (this.#components.has(component.resolveable))
throw new Error(`Attempted to reload an existing component: ${component.resolveable}`);
throw new Error(`Attempted to load an existing component: ${component.resolveable}`);
if (directory)
component.directory = directory;

View File

@ -0,0 +1,42 @@
import { CommandOpts, OptionType } from '@navy.gif/commandparser';
import Command from '../../../interfaces/Command.js';
import DiscordClient from '../../DiscordClient.js';
import { Message } from 'discord.js';
class ListCommand extends Command
{
constructor (client: DiscordClient)
{
super(client, {
name: 'list',
description: 'Display a list of things on the bot.',
options: [{
name: 'commands',
type: OptionType.SUB_COMMAND,
options: [{
name: 'all',
type: OptionType.BOOLEAN,
valueOptional: true,
defaultValue: true,
flag: true
}]
}]
});
}
async execute (_message: Message, { subcommand, args }: CommandOpts)
{
const { all } = args;
if (subcommand === 'commands')
return this.#listCommands(all?.value as boolean);
return 'Invalid subcommand';
}
#listCommands (all?: boolean)
{
const commands= this.client.registry.commands.filter(cmd => !cmd.restricted || all);
return `**Available commands**\n*Use \`${this.client.prefix}command --help\` to display command options*\n${commands.map(cmd => `\t\\- __${cmd.name}__: ${cmd.description}`).join('\n')}`;
}
}
export default ListCommand;

View File

@ -6,7 +6,8 @@ class PingCommand extends Command
constructor (client: DiscordClient)
{
super(client, {
name: 'ping'
name: 'ping',
description: 'pong'
});
}

View File

@ -10,6 +10,7 @@ class QueueCommand extends Command
super(client, {
name: 'queue',
aliases: [ 'q' ],
description: 'Queue a song (must be already downloaded, use search to look up songs), or display the current queue.',
options: [{
name: 'artist',
flag: true

View File

@ -12,6 +12,7 @@ class RequestCommand extends Command
super(client, {
name: 'request',
aliases: [ 'r', 'req' ],
description: 'Request a song to be downloaded and queued.',
guildOnly: true,
sameVc: true,
showUsage: true,

View File

@ -10,6 +10,7 @@ class PingCommand extends Command
super(client, {
name: 'rescan',
restricted: true,
description: 'Rescan the library and add to index.',
options: [{
name: 'rebuild',
type: OptionType.BOOLEAN,

View File

@ -7,6 +7,7 @@ class ReshuffleCommand extends Command
{
super(client, {
name: 'reshuffle',
description: 'Reshuffles the playlist (does not apply to queue).',
sameVc: true,
guildOnly: true,
});

View File

@ -9,7 +9,8 @@ class SearchCommand extends Command
{
super(client, {
name: 'search',
aliases: [ 's' ],
aliases: [ 's', 'query' ],
description: 'Search the index for an already downloaded song.',
showUsage: true,
options: [{
name: 'keyword',

View File

@ -9,6 +9,7 @@ class SetAvatarCommand extends Command
{
super(client, {
name: 'set',
description: 'Sets various things on the bot.',
options: [{
name: 'avatar',
type: OptionType.SUB_COMMAND,

View File

@ -8,6 +8,7 @@ class SkipCommand extends Command
{
super(client, {
name: 'skip',
description: 'Skips the current song.',
guildOnly: true,
restricted: true,
sameVc: true

View File

@ -10,6 +10,7 @@ class VolumeCommand extends Command
super(client, {
name: 'volume',
aliases: [ 'v', 'vol' ],
description: 'Display or set the volume.',
options: [
{
name: 'volume',

View File

@ -30,6 +30,7 @@ class CommandHandler extends Observer
commands: this.client.commands.values(),
prefix: client.prefix,
resolver: client.resolver,
allowIncompleteReturn: true,
globalFlags: [{
name: 'help',
flag: true,
@ -47,6 +48,8 @@ class CommandHandler extends Observer
{
const allowedMentions = { repliedUser: false };
const { author } = message;
if (author.bot)
return;
let result = null,
inhibitions: InhibitorResponse[] | null = null;
try
@ -128,10 +131,10 @@ class CommandHandler extends Observer
}
if (rest.globalFlags.help)
return this.#showUsage(message, command as Command);
return this.#showUsage(message, command as Command, rest);
if ((command as Command).showUsage && !Object.keys(rest.args).length && !rest.subcommand && !rest.subcommandGroup)
return this.#showUsage(message, command as Command);
return this.#showUsage(message, command as Command, rest);
this.#executeCommand(message, command, rest);
}
@ -234,14 +237,39 @@ class CommandHandler extends Observer
}
}
#showUsage (message: Message<boolean>, command: Command): void | PromiseLike<void>
#showUsage (message: Message<boolean>, command: Command, opts: CommandOpts): void | PromiseLike<void>
{
const { options } = command;
const { subcommand, subcommandGroup } = opts;
let sbcmdstr = '';
if (subcommandGroup)
sbcmdstr += `${subcommand}`;
if (subcommand)
sbcmdstr += ` ${subcommand}`;
sbcmdstr = sbcmdstr.trim();
let { options } = command;
let usageStr = `${this.client.prefix}${command.name} `;
if (sbcmdstr)
usageStr += sbcmdstr;
else if (options.length)
usageStr += '[OPTIONS]';
let output = stripIndents`
USAGE: \`${usageStr.trim()} [FLAGS]\`
`;
if (subcommand)
{
const sbcmd = command.subcommand(subcommand);
({ options } = sbcmd!);
}
else if (subcommandGroup)
{
const group = command.subcommandGroup(subcommandGroup);
({ options } = group!);
}
const flags = options.filter(option => option.flag);
const nonFlags = options.filter(option => !option.flag);
let output = stripIndents`
USAGE: \`${this.client.prefix}${command.name} [OPTIONS] [FLAGS]\`
`;
if (nonFlags.length)
{

View File

@ -11,6 +11,7 @@ abstract class Command extends Component implements ICommand
#aliases: string[];
#options: CommandOption[];
#help?: string | undefined;
#description?: string;
#logger: LoggerClient;
@ -35,6 +36,7 @@ abstract class Command extends Component implements ICommand
this.#dmOnly = def.dmOnly ?? false;
this.#limited = def.limited ?? null;
this.#showUsage = def.showUsage ?? false;
this.#description = def.description;
this.#options = [];
@ -69,6 +71,11 @@ abstract class Command extends Component implements ICommand
throw new CommandError(this, { reason: 'Command timed out', user });
}
get description ()
{
return this.#description ?? '[No description given]';
}
get showUsage ()
{
return this.#showUsage;

View File

@ -1628,10 +1628,10 @@ __metadata:
languageName: node
linkType: hard
"@navy.gif/commandparser@npm:^1.6.7":
version: 1.6.7
resolution: "@navy.gif/commandparser@npm:1.6.7"
checksum: 10/2f3ac85ca0d7168af96fd5d03ff85e00949e8f02ff7ac409e01f492c9712442723df536103897bed7d3c3091066525e78254e36591e49d1a9cade16f472383e9
"@navy.gif/commandparser@npm:^1.6.8":
version: 1.6.8
resolution: "@navy.gif/commandparser@npm:1.6.8"
checksum: 10/53f2d4f9a8f98a83d2304297bcabbe7defe5cf5500024f9d9c63efb4d0e631fe6545b2621421b43f05f8e9af1a2dad3ac79b5898faceb995fbb5b7db2cb3bb1d
languageName: node
linkType: hard
@ -3577,7 +3577,7 @@ __metadata:
"@babel/preset-typescript": "npm:^7.24.1"
"@discordjs/opus": "npm:^0.9.0"
"@discordjs/voice": "npm:^0.16.1"
"@navy.gif/commandparser": "npm:^1.6.7"
"@navy.gif/commandparser": "npm:^1.6.8"
"@navy.gif/logger": "npm:^2.5.4"
"@navy.gif/timestring": "npm:^6.0.6"
"@types/babel__core": "npm:^7"