@julanzw/ttoolbox-discordjs-framework 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +675 -0
  2. package/README.md +77 -0
  3. package/dist/classes/Command.class.d.ts +169 -0
  4. package/dist/classes/Command.class.js +156 -0
  5. package/dist/classes/CommandManager.class.d.ts +69 -0
  6. package/dist/classes/CommandManager.class.js +149 -0
  7. package/dist/classes/DiscordHandler.class.d.ts +241 -0
  8. package/dist/classes/DiscordHandler.class.js +222 -0
  9. package/dist/classes/InteractionError.class.d.ts +8 -0
  10. package/dist/classes/InteractionError.class.js +11 -0
  11. package/dist/classes/ModalManager.class.d.ts +154 -0
  12. package/dist/classes/ModalManager.class.js +205 -0
  13. package/dist/classes/SubcommandGroup.class.d.ts +127 -0
  14. package/dist/classes/SubcommandGroup.class.js +156 -0
  15. package/dist/index.d.ts +18 -0
  16. package/dist/index.js +17 -0
  17. package/dist/types/button.d.ts +2 -0
  18. package/dist/types/button.js +1 -0
  19. package/dist/types/channel.d.ts +2 -0
  20. package/dist/types/channel.js +1 -0
  21. package/dist/types/logger.d.ts +37 -0
  22. package/dist/types/logger.js +1 -0
  23. package/dist/types/modal.d.ts +77 -0
  24. package/dist/types/modal.js +1 -0
  25. package/dist/types/permission.d.ts +2 -0
  26. package/dist/types/permission.js +1 -0
  27. package/dist/utils/PaginatedEmbed.class.d.ts +25 -0
  28. package/dist/utils/PaginatedEmbed.class.js +102 -0
  29. package/dist/utils/TToolboxLogger.class.d.ts +176 -0
  30. package/dist/utils/TToolboxLogger.class.js +252 -0
  31. package/dist/utils/cooldown.d.ts +13 -0
  32. package/dist/utils/cooldown.js +43 -0
  33. package/dist/utils/editAndReply.d.ts +37 -0
  34. package/dist/utils/editAndReply.js +85 -0
  35. package/dist/utils/embeds.d.ts +55 -0
  36. package/dist/utils/embeds.js +94 -0
  37. package/dist/utils/formatting.d.ts +44 -0
  38. package/dist/utils/formatting.js +87 -0
  39. package/dist/utils/miliseconds.d.ts +10 -0
  40. package/dist/utils/miliseconds.js +11 -0
  41. package/dist/utils/permissions.d.ts +8 -0
  42. package/dist/utils/permissions.js +24 -0
  43. package/dist/utils/slashCommandOptions.d.ts +8 -0
  44. package/dist/utils/slashCommandOptions.js +11 -0
  45. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # TToolbox Discord Framework
2
+
3
+ A TypeScript-first Discord.js command framework with built-in handlers, logging, and utilities.
4
+
5
+ I made this for my own bots, but feel free to use it yourself!
6
+
7
+ ## Features
8
+
9
+ - **Class-based commands** - Clean, extensible command structure
10
+ - **Permission system** - Built-in user/admin/owner permission levels
11
+ - **Logger** - Flexible logging with custom levels and colors
12
+ - **Pagination** - Easy paginated embeds with buttons
13
+ - **Modal helpers** - Simplified modal management
14
+ - **Error handling** - Built-in error handling and validation
15
+ - **TypeScript** - Full type safety
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @julanzw/ttoolbox-discord-framework discord.js
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { Client, GatewayIntentBits } from 'discord.js';
27
+ import { Command, CommandManager, TToolboxLogger } from '@julanzw/ttoolbox-discord-framework';
28
+
29
+ const client = new Client({
30
+ intents: [GatewayIntentBits.Guilds]
31
+ });
32
+
33
+ const logger = new TToolboxLogger();
34
+ const commandManager = new CommandManager();
35
+
36
+ // Create a command
37
+ class PingCommand extends Command {
38
+ name = 'ping';
39
+ description = 'Pong!';
40
+ guildOnly = false;
41
+ permissionLevel = 'user' as const;
42
+
43
+ protected async run(interaction) {
44
+ await interaction.reply('Pong!');
45
+ }
46
+ }
47
+
48
+ // Register commands
49
+ commandManager
50
+ .setLogger(logger)
51
+ .register(new PingCommand());
52
+
53
+ // Handle interactions
54
+ client.on('interactionCreate', async (interaction) => {
55
+ if (interaction.isChatInputCommand()) {
56
+ await commandManager.executeCommand(
57
+ interaction.commandName,
58
+ interaction,
59
+ client
60
+ );
61
+ }
62
+ });
63
+
64
+ client.login(process.env.DISCORD_TOKEN);
65
+ ```
66
+
67
+ ## Documentation
68
+
69
+ (hopefully soon)
70
+
71
+ ## License
72
+
73
+ AGPL-3.0 - See [LICENSE](./LICENSE) for details
74
+
75
+ ## Contributing
76
+
77
+ Issues and pull requests are welcome!
@@ -0,0 +1,169 @@
1
+ import { ChatInputCommandInteraction, Client, SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js';
2
+ import { PermissionLevel } from '../types/permission.js';
3
+ import { ILogger } from '../types/logger.js';
4
+ /**
5
+ * Abstract base class for Discord slash commands.
6
+ *
7
+ * Provides a structured way to create slash commands with built-in validation,
8
+ * error handling, and permission management. Child classes should extend this
9
+ * and implement the required abstract properties and the `run` method.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * export class PingCommand extends Command {
14
+ * name = 'ping';
15
+ * description = 'Check bot latency';
16
+ * guildOnly = false;
17
+ * permissionLevel = 'user' as const;
18
+ *
19
+ * protected async run(interaction: ChatInputCommandInteraction) {
20
+ * await safeReply(interaction, `Pong! ${interaction.client.ws.ping}ms`);
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+ export declare abstract class Command {
26
+ /** The command name (used to invoke the command) */
27
+ abstract name: string;
28
+ /** A brief description of what the command does */
29
+ abstract description: string;
30
+ /** Whether this command can only be used in a guild (server) */
31
+ abstract guildOnly: boolean;
32
+ /** The minimum permission level required to use this command */
33
+ abstract permissionLevel: PermissionLevel;
34
+ /** Optional cooldown in milliseconds between command uses per user */
35
+ cooldown?: number;
36
+ /** Logger instance to use inside the command */
37
+ protected logger?: ILogger;
38
+ /**
39
+ * Validates whether the command can be executed in the current context.
40
+ *
41
+ * Checks if the command is being used in the correct context (guild vs DM)
42
+ * and calls additionalValidation for any custom validation logic.
43
+ *
44
+ * @param interaction - The command interaction to validate
45
+ * @returns Error message if validation fails, null if validation passes
46
+ * @protected
47
+ */
48
+ protected validate(interaction: ChatInputCommandInteraction): string | null;
49
+ /**
50
+ * Hook for additional custom validation logic.
51
+ *
52
+ * Override this method in child classes to add command-specific validation.
53
+ * For example, checking if a user has opted in/out, premium status, etc.
54
+ *
55
+ * @param interaction - The command interaction to validate
56
+ * @returns Error message if validation fails, null if validation passes
57
+ * @protected
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * protected additionalValidation(interaction: ChatInputCommandInteraction): string | null {
62
+ * if (hasOptedOut(interaction.user.id)) {
63
+ * return 'You have opted out of this feature.';
64
+ * }
65
+ * return null;
66
+ * }
67
+ * ```
68
+ */
69
+ protected additionalValidation(interaction: ChatInputCommandInteraction): string | null;
70
+ /**
71
+ * Safely executes a function with error handling and logging.
72
+ *
73
+ * Wraps the execution in a try-catch block, logs successful executions,
74
+ * and automatically handles errors by logging them and sending a user-friendly
75
+ * error message.
76
+ *
77
+ * @param commandName - The name of the command being executed
78
+ * @param interaction - The command interaction
79
+ * @param fn - The function to execute
80
+ * @private
81
+ */
82
+ private safeExecute;
83
+ /**
84
+ * Executes the command with validation and error handling.
85
+ *
86
+ * This is the main entry point when a command is invoked. It performs
87
+ * validation, then calls the `run` method if validation passes.
88
+ * Should not be overridden - override `run` instead.
89
+ *
90
+ * @param interaction - The command interaction
91
+ * @param client - The Discord client instance
92
+ */
93
+ execute(interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
94
+ /**
95
+ * The main command logic - implement this in child classes.
96
+ *
97
+ * This method is called after all validation passes. Put your command's
98
+ * actual functionality here.
99
+ *
100
+ * @param interaction - The command interaction
101
+ * @param client - The Discord client instance
102
+ * @protected
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * protected async run(interaction: ChatInputCommandInteraction) {
107
+ * const user = interaction.options.getUser('user', true);
108
+ * await safeReply(interaction, `Hello, ${user.tag}!`);
109
+ * }
110
+ * ```
111
+ */
112
+ protected abstract run(interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
113
+ /**
114
+ * Customize the slash command builder with options, choices, etc.
115
+ *
116
+ * Override this method to add options (string, integer, user, etc.) to your command.
117
+ * Works with both standalone commands and subcommands.
118
+ *
119
+ * @param builder - The slash command builder to customize
120
+ * @returns The customized builder
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * customize(builder: SlashCommandBuilder) {
125
+ * return builder
126
+ * .addStringOption(option =>
127
+ * option
128
+ * .setName('message')
129
+ * .setDescription('The message to send')
130
+ * .setRequired(true)
131
+ * )
132
+ * .addUserOption(option =>
133
+ * option
134
+ * .setName('user')
135
+ * .setDescription('The user to mention')
136
+ * .setRequired(false)
137
+ * );
138
+ * }
139
+ * ```
140
+ */
141
+ customize?(builder: SlashCommandBuilder | SlashCommandSubcommandBuilder): SlashCommandBuilder | SlashCommandSubcommandBuilder;
142
+ /**
143
+ * Converts the command to Discord API JSON format for registration.
144
+ *
145
+ * This method builds the SlashCommandBuilder with the command's metadata
146
+ * (name, description, permissions) and any custom options, then returns
147
+ * the JSON representation needed for Discord's API.
148
+ *
149
+ * Called automatically by CommandManager when registering commands.
150
+ *
151
+ * @returns The command in Discord API JSON format
152
+ */
153
+ toJSON(): import("discord.js").RESTPostAPIChatInputApplicationCommandsJSONBody;
154
+ /**
155
+ * Sets the logger for this command.
156
+ *
157
+ * @param logger - Logger instance implementing ILogger interface
158
+ */
159
+ setLogger(logger: ILogger): void;
160
+ /**
161
+ * Log a message using the configured logger.
162
+ *
163
+ * @param message - The message to log
164
+ * @param level - The log level
165
+ * @param scope - The scope/context
166
+ * @param logToConsole - Whether to also log to console
167
+ */
168
+ protected log(message: string, level: string, scope: string, logToConsole?: boolean): void;
169
+ }
@@ -0,0 +1,156 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { SlashCommandBuilder, } from 'discord.js';
3
+ import { safeReply } from '../utils/editAndReply.js';
4
+ import { getPermissionsForLevel } from '../utils/permissions.js';
5
+ import { checkCooldown } from '../utils/cooldown.js';
6
+ import { formatDuration } from '../utils/formatting.js';
7
+ /**
8
+ * Abstract base class for Discord slash commands.
9
+ *
10
+ * Provides a structured way to create slash commands with built-in validation,
11
+ * error handling, and permission management. Child classes should extend this
12
+ * and implement the required abstract properties and the `run` method.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * export class PingCommand extends Command {
17
+ * name = 'ping';
18
+ * description = 'Check bot latency';
19
+ * guildOnly = false;
20
+ * permissionLevel = 'user' as const;
21
+ *
22
+ * protected async run(interaction: ChatInputCommandInteraction) {
23
+ * await safeReply(interaction, `Pong! ${interaction.client.ws.ping}ms`);
24
+ * }
25
+ * }
26
+ * ```
27
+ */
28
+ export class Command {
29
+ /**
30
+ * Validates whether the command can be executed in the current context.
31
+ *
32
+ * Checks if the command is being used in the correct context (guild vs DM)
33
+ * and calls additionalValidation for any custom validation logic.
34
+ *
35
+ * @param interaction - The command interaction to validate
36
+ * @returns Error message if validation fails, null if validation passes
37
+ * @protected
38
+ */
39
+ validate(interaction) {
40
+ if (this.guildOnly && !interaction.guildId) {
41
+ return 'This command can only be used in a server.';
42
+ }
43
+ const secondsRemaining = checkCooldown(this.name, this.cooldown, interaction.user.id);
44
+ if (secondsRemaining > 0) {
45
+ return `You need to wait ${formatDuration(secondsRemaining)} before using this command again.`;
46
+ }
47
+ return this.additionalValidation(interaction);
48
+ }
49
+ /**
50
+ * Hook for additional custom validation logic.
51
+ *
52
+ * Override this method in child classes to add command-specific validation.
53
+ * For example, checking if a user has opted in/out, premium status, etc.
54
+ *
55
+ * @param interaction - The command interaction to validate
56
+ * @returns Error message if validation fails, null if validation passes
57
+ * @protected
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * protected additionalValidation(interaction: ChatInputCommandInteraction): string | null {
62
+ * if (hasOptedOut(interaction.user.id)) {
63
+ * return 'You have opted out of this feature.';
64
+ * }
65
+ * return null;
66
+ * }
67
+ * ```
68
+ */
69
+ additionalValidation(interaction) {
70
+ return null;
71
+ }
72
+ /**
73
+ * Safely executes a function with error handling and logging.
74
+ *
75
+ * Wraps the execution in a try-catch block, logs successful executions,
76
+ * and automatically handles errors by logging them and sending a user-friendly
77
+ * error message.
78
+ *
79
+ * @param commandName - The name of the command being executed
80
+ * @param interaction - The command interaction
81
+ * @param fn - The function to execute
82
+ * @private
83
+ */
84
+ async safeExecute(commandName, interaction, fn) {
85
+ const scope = `${commandName}_EXECUTION`;
86
+ try {
87
+ await fn();
88
+ const subcommandName = interaction.options.getSubcommand(false);
89
+ this.logger?.log(`${commandName} ${subcommandName ? `(${subcommandName}) ` : ``}command executed`, 'info', scope);
90
+ }
91
+ catch (err) {
92
+ this.logger?.log(
93
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
94
+ `An Error occurred: ${err.message ?? err}`, 'error', scope, true);
95
+ return await safeReply(interaction, 'An unexpected error occurred.');
96
+ }
97
+ }
98
+ /**
99
+ * Executes the command with validation and error handling.
100
+ *
101
+ * This is the main entry point when a command is invoked. It performs
102
+ * validation, then calls the `run` method if validation passes.
103
+ * Should not be overridden - override `run` instead.
104
+ *
105
+ * @param interaction - The command interaction
106
+ * @param client - The Discord client instance
107
+ */
108
+ async execute(interaction, client) {
109
+ await this.safeExecute(this.name, interaction, async () => {
110
+ const error = this.validate(interaction);
111
+ if (error)
112
+ return await safeReply(interaction, error, true);
113
+ await this.run(interaction, client);
114
+ });
115
+ }
116
+ /**
117
+ * Converts the command to Discord API JSON format for registration.
118
+ *
119
+ * This method builds the SlashCommandBuilder with the command's metadata
120
+ * (name, description, permissions) and any custom options, then returns
121
+ * the JSON representation needed for Discord's API.
122
+ *
123
+ * Called automatically by CommandManager when registering commands.
124
+ *
125
+ * @returns The command in Discord API JSON format
126
+ */
127
+ toJSON() {
128
+ const builder = new SlashCommandBuilder()
129
+ .setName(this.name)
130
+ .setDescription(this.description)
131
+ .setDefaultMemberPermissions(getPermissionsForLevel(this.permissionLevel));
132
+ if (this.customize) {
133
+ this.customize(builder);
134
+ }
135
+ return builder.toJSON();
136
+ }
137
+ /**
138
+ * Sets the logger for this command.
139
+ *
140
+ * @param logger - Logger instance implementing ILogger interface
141
+ */
142
+ setLogger(logger) {
143
+ this.logger = logger;
144
+ }
145
+ /**
146
+ * Log a message using the configured logger.
147
+ *
148
+ * @param message - The message to log
149
+ * @param level - The log level
150
+ * @param scope - The scope/context
151
+ * @param logToConsole - Whether to also log to console
152
+ */
153
+ log(message, level, scope, logToConsole = false) {
154
+ this.logger?.log(message, level, scope, logToConsole);
155
+ }
156
+ }
@@ -0,0 +1,69 @@
1
+ import { ChatInputCommandInteraction, Client, RESTPostAPIChatInputApplicationCommandsJSONBody } from 'discord.js';
2
+ import { ILogger } from '../types/logger.js';
3
+ import { Command } from './Command.class.js';
4
+ import { SubcommandGroup } from './SubcommandGroup.class.js';
5
+ export declare class CommandManager {
6
+ private commands;
7
+ protected logger?: ILogger;
8
+ /**
9
+ * Register a single command or subcommand group
10
+ */
11
+ register(command: Command | SubcommandGroup): this;
12
+ /**
13
+ * Register multiple commands at once
14
+ */
15
+ registerMultiple(commands: Array<Command | SubcommandGroup>): this;
16
+ /**
17
+ * Get a specific command by name
18
+ */
19
+ get(name: string): Command | SubcommandGroup | undefined;
20
+ /**
21
+ * Get all registered commands
22
+ */
23
+ getAll(): Array<Command | SubcommandGroup>;
24
+ /**
25
+ * Get all commands sorted alphabetically by name
26
+ */
27
+ getAllSorted(): Array<Command | SubcommandGroup>;
28
+ /**
29
+ * Convert all commands to Discord JSON format for registration
30
+ */
31
+ toDiscordJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody[];
32
+ /**
33
+ * Generate paginated help pages for display in help command
34
+ * Returns a 2D array where each inner array is a page of command descriptions
35
+ *
36
+ * @param commandsPerPage - How many regular commands to show per page (default: 10)
37
+ * @returns 2D array of command info for pagination
38
+ */
39
+ getHelpPages(commandsPerPage?: number): Array<Array<{
40
+ name: string;
41
+ value: string;
42
+ }>>;
43
+ /**
44
+ * Execute a command by name
45
+ * This is called from your interaction handler
46
+ */
47
+ executeCommand(commandName: string, interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
48
+ /**
49
+ * Get total number of registered commands
50
+ */
51
+ get size(): number;
52
+ /**
53
+ * Check if a command exists
54
+ */
55
+ has(name: string): boolean;
56
+ /**
57
+ * Remove a command (useful for hot-reloading in dev)
58
+ */
59
+ unregister(name: string): boolean;
60
+ /**
61
+ * Clear all commands
62
+ */
63
+ clear(): void;
64
+ /**
65
+ * Get command names as an array
66
+ */
67
+ getCommandNames(): string[];
68
+ setLogger(logger: ILogger): this;
69
+ }
@@ -0,0 +1,149 @@
1
+ import { SubcommandGroup } from './SubcommandGroup.class.js';
2
+ export class CommandManager {
3
+ constructor() {
4
+ this.commands = new Map();
5
+ }
6
+ /**
7
+ * Register a single command or subcommand group
8
+ */
9
+ register(command) {
10
+ if (this.logger) {
11
+ command.setLogger(this.logger);
12
+ }
13
+ this.commands.set(command.name, command);
14
+ return this;
15
+ }
16
+ /**
17
+ * Register multiple commands at once
18
+ */
19
+ registerMultiple(commands) {
20
+ commands.forEach((cmd) => this.register(cmd));
21
+ return this;
22
+ }
23
+ /**
24
+ * Get a specific command by name
25
+ */
26
+ get(name) {
27
+ return this.commands.get(name);
28
+ }
29
+ /**
30
+ * Get all registered commands
31
+ */
32
+ getAll() {
33
+ return Array.from(this.commands.values());
34
+ }
35
+ /**
36
+ * Get all commands sorted alphabetically by name
37
+ */
38
+ getAllSorted() {
39
+ return this.getAll().sort((a, b) => a.name.localeCompare(b.name));
40
+ }
41
+ /**
42
+ * Convert all commands to Discord JSON format for registration
43
+ */
44
+ toDiscordJSON() {
45
+ return this.getAll().map((cmd) => {
46
+ if (process.env.ENV === 'dev') {
47
+ console.log(`Registering: ${cmd.name}`);
48
+ }
49
+ return cmd.toJSON();
50
+ });
51
+ }
52
+ /**
53
+ * Generate paginated help pages for display in help command
54
+ * Returns a 2D array where each inner array is a page of command descriptions
55
+ *
56
+ * @param commandsPerPage - How many regular commands to show per page (default: 10)
57
+ * @returns 2D array of command info for pagination
58
+ */
59
+ getHelpPages(commandsPerPage = 5) {
60
+ const subcommandPages = [];
61
+ const otherCommands = [];
62
+ for (const command of this.getAllSorted()) {
63
+ if (command instanceof SubcommandGroup) {
64
+ // Each subcommand group gets its own page
65
+ const page = [
66
+ {
67
+ name: `─── ${command.name.toUpperCase()} ───`,
68
+ value: command.description || 'No description.',
69
+ },
70
+ ...command.getSubcommandList().map((sub) => ({
71
+ name: `› ${sub.name}`,
72
+ value: sub.description,
73
+ })),
74
+ ];
75
+ subcommandPages.push(page);
76
+ }
77
+ else {
78
+ // Regular commands are grouped together
79
+ // Add a header when starting a new page
80
+ if (otherCommands.length % commandsPerPage === 0) {
81
+ otherCommands.push({
82
+ name: `─── OTHER ───`,
83
+ value: 'Other commands',
84
+ });
85
+ }
86
+ otherCommands.push({
87
+ name: `› ${command.name}`,
88
+ value: command.description,
89
+ });
90
+ }
91
+ }
92
+ // Combine subcommand pages first, then regular command pages
93
+ const allPages = [...subcommandPages];
94
+ // Split regular commands into pages
95
+ while (otherCommands.length) {
96
+ allPages.push(otherCommands.splice(0, commandsPerPage));
97
+ }
98
+ return allPages;
99
+ }
100
+ /**
101
+ * Execute a command by name
102
+ * This is called from your interaction handler
103
+ */
104
+ async executeCommand(commandName, interaction, client) {
105
+ const command = this.get(commandName);
106
+ if (!command) {
107
+ throw new Error(`Command not found: ${commandName}`);
108
+ }
109
+ await command.execute(interaction, client);
110
+ }
111
+ /**
112
+ * Get total number of registered commands
113
+ */
114
+ get size() {
115
+ return this.commands.size;
116
+ }
117
+ /**
118
+ * Check if a command exists
119
+ */
120
+ has(name) {
121
+ return this.commands.has(name);
122
+ }
123
+ /**
124
+ * Remove a command (useful for hot-reloading in dev)
125
+ */
126
+ unregister(name) {
127
+ return this.commands.delete(name);
128
+ }
129
+ /**
130
+ * Clear all commands
131
+ */
132
+ clear() {
133
+ this.commands.clear();
134
+ }
135
+ /**
136
+ * Get command names as an array
137
+ */
138
+ getCommandNames() {
139
+ return Array.from(this.commands.keys());
140
+ }
141
+ setLogger(logger) {
142
+ this.logger = logger;
143
+ // Inject logger into all already-registered commands
144
+ for (const command of this.commands.values()) {
145
+ command.setLogger(logger);
146
+ }
147
+ return this;
148
+ }
149
+ }