Skip to content

Commit cd4d69f

Browse files
committed
✨ Command sub-directories
1 parent 354dbe8 commit cd4d69f

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed

README.md

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import ModuleLoader, { DiscordEvent } from "discord-module-loader";
3030
```js
3131
// Here we're importing the default export "ModuleLoader", which is the main class which will load all modules
3232
// as well as the DiscordEvent class which is used to add event listeners to the main class
33-
const { DiscordEvent, default: ModuleLoader } = require("discord-module-loader");
33+
const {
34+
DiscordEvent,
35+
default: ModuleLoader
36+
} = require("discord-module-loader");
3437
```
3538

3639
Or, if you only want the main class ModuleLoader
@@ -46,6 +49,7 @@ const { DiscordEvent } = require("discord-module-loader");
4649
```
4750

4851
### Using our module loader
52+
4953
To use the ModuleLoader, your bot has to use the following file structure:
5054

5155
```
@@ -58,49 +62,57 @@ To use the ModuleLoader, your bot has to use the following file structure:
5862
```
5963

6064
#### `commands` folder
65+
6166
Inside of the `commands` folder you can put as many command files as you want!
6267
We suggest you put the command name as the file name, but it is not required.
68+
The commands folder also accepts sub-directories, meaning that in the commands folder you could put a folder named anything, and put commands inside of there.
6369
In those files the following must be exported:
70+
6471
```js
6572
import { DiscordCommand } from "discord-module-loader";
6673

6774
export default new DiscordCommand({
6875
name: "name",
6976
description: "Example command",
70-
execute: async (int) => {
77+
execute: async int => {
7178
console.log(int);
7279
}
73-
})
80+
});
7481
```
82+
7583
In the `DiscordCommand` class you have to pass an object with the following arguments:
7684

77-
| Argument | Description | Required | Type |
78-
|---|---|---|---|
79-
| `name` | Name of the command | Yes | `string` |
80-
| `description` | Description of the command | Yes | `string` |
81-
| `defaultPermission` | Whether the command is enabled by default when the app is added to a guild | No | `boolean` |
82-
| `options` | The options of the command | No | [`ApplicationCommandOptionData`](https://discord.js.org/#/docs/discord.js/stable/typedef/ApplicationCommandOptionData)`[]` |
83-
| `cooldown` | The amount of time in seconds a user has to wait between command executions | No | `number` |
84-
| `channelAllowlist` | Array of Discord channel ids where the command is allowed to be executed in | No | `string[]` |
85-
| `channelDenylist` | Array of Discord channel ids where the command is not allowed to be executed in | No | `string[]` |
86-
| `permissions` | Permission data of the command | No | [`ApplicationCommandPermissionData`](https://discord.js.org/#/docs/discord.js/stable/typedef/ApplicationCommandPermissionData)`[]` |
87-
| `hasUserCommand` | Whether the command has a user context menu | No | `boolean` |
88-
| `execute` | The function which will be run when the command is executed | Yes | `Function (interaction:`[`CommandInteraction`](https://discord.js.org/#/docs/discord.js/stable/class/CommandInteraction)`)` |
85+
| Argument | Description | Required | Type |
86+
| ------------------- | ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
87+
| `name` | Name of the command | Yes | `string` |
88+
| `description` | Description of the command | Yes | `string` |
89+
| `defaultPermission` | Whether the command is enabled by default when the app is added to a guild | No | `boolean` |
90+
| `options` | The options of the command | No | [`ApplicationCommandOptionData`](https://discord.js.org/#/docs/discord.js/stable/typedef/ApplicationCommandOptionData)`[]` |
91+
| `cooldown` | The amount of time in seconds a user has to wait between command executions | No | `number` |
92+
| `channelAllowlist` | Array of Discord channel ids where the command is allowed to be executed in | No | `string[]` |
93+
| `channelDenylist` | Array of Discord channel ids where the command is not allowed to be executed in | No | `string[]` |
94+
| `permissions` | Permission data of the command | No | [`ApplicationCommandPermissionData`](https://discord.js.org/#/docs/discord.js/stable/typedef/ApplicationCommandPermissionData)`[]` |
95+
| `hasUserCommand` | Whether the command has a user context menu | No | `boolean` |
96+
| `execute` | The function which will be run when the command is executed | Yes | `Function (interaction:`[`CommandInteraction`](https://discord.js.org/#/docs/discord.js/stable/class/CommandInteraction)`)` |
8997

9098
#### `events` folder
99+
91100
Inside of the `events` folder you can put as many event files as you want!
92101
We suggest you put the event name as the file name, but it is not required.
93102
In those files the following must be exported:
103+
94104
```ts
95105
import { DiscordEvent } from "discord-module-loader";
96106

97107
export default new DiscordEvent("messageCreate", message => {
98108
console.log(message.content);
99109
});
100110
```
111+
101112
`"messageCreate"` can be changed for any event name just like the `message` variable can be changed to the incoming data of the event.
102113

103114
#### `modules` folder
115+
104116
To use the `DiscordModule` class you have to use the following file structure:
105117

106118
```
@@ -111,8 +123,10 @@ To use the `DiscordModule` class you have to use the following file structure:
111123
├─ modules
112124
╰─ index
113125
```
126+
114127
The commands, events, and modules folders are just like their above specified ones.
115128
In the index file the following must be exported:
129+
116130
```ts
117131
import { DiscordModule } from "discord-module-loader";
118132

@@ -122,6 +136,7 @@ export default new DiscordModule("<module name>");
122136
In the `DiscordModule` class you only have to specify the name you want the module to have.
123137

124138
#### `guilds` folder
139+
125140
To use the `DiscordGuild` class you have to use the following file structure:
126141

127142
```
@@ -135,6 +150,7 @@ To use the `DiscordGuild` class you have to use the following file structure:
135150

136151
The commands, events, and modules folders are just like their above specified ones.
137152
But the index one is of course a lil' different! In the index file the following must be exported:
153+
138154
```ts
139155
import { DiscordGuild } from "discord-module-loader";
140156

@@ -144,9 +160,12 @@ export default new DiscordGuild("<guildId>");
144160
In the `DiscordGuild` class you only have the specify the guildId wherein the commands, events, and modules should work.
145161

146162
#### `index` file
163+
147164
```js
148165
// Firstly we must create the normal Discord Client from Discord.js
149-
const client = new Client({ intents: [Intents.Flags.GUILD, Intents.Flags.GUILD_MESSAGES] });
166+
const client = new Client({
167+
intents: [Intents.Flags.GUILD, Intents.Flags.GUILD_MESSAGES]
168+
});
150169

151170
// Afterwards we can initialize the module loader with the Discord.js Client
152171
// a few options could be appended after the client, more information about the ModuleLoader options can be found further below
@@ -160,22 +179,22 @@ client.on("ready", async () => {
160179
await moduleLoader.loadAll();
161180
await moduleLoader.updateSlashCommands();
162181
});
163-
```
182+
```
164183

165184
In the `ModuleLoader` class you can pass an object with the following arguments:
166185
(All arguments are optional)
167186

168-
| Argument | Description | Type | Default Value |
169-
|---|---|---|---|
170-
| `unknownCommandMessage` | The message shown when a user executes an unknown command | `string` | Couldn't find executed command. Please try again later, or report the issue. |
171-
| `disabledCommandMessage` | The message shown when a user executes a disabled command | `string` | This command is currently disabled. Please try again later. |
172-
| `disallowedChannelMessage` | The message shown when a user executes a command in a disallowed channel | `string` | You're not allowed to execute this command in this channel! |
173-
| `commandCooldownMessage` | The message shown when a user executes a command while on cooldown | `string` | Please wait % seconds before using this command again. |
187+
| Argument | Description | Type | Default Value |
188+
| -------------------------- | ------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------- |
189+
| `unknownCommandMessage` | The message shown when a user executes an unknown command | `string` | Couldn't find executed command. Please try again later, or report the issue. |
190+
| `disabledCommandMessage` | The message shown when a user executes a disabled command | `string` | This command is currently disabled. Please try again later. |
191+
| `disallowedChannelMessage` | The message shown when a user executes a command in a disallowed channel | `string` | You're not allowed to execute this command in this channel! |
192+
| `commandCooldownMessage` | The message shown when a user executes a command while on cooldown | `string` | Please wait % seconds before using this command again. |
174193

175194
## Contributing
176195

177196
Due to Discord and Discord.js always updating, it is possible some things might break. If you believe you have found and issue, feel free to [open a pull request](https://github.com/Recodive/Discord-Module-Loader/compare).
178197

179198
## Inspiration
180199

181-
Due to the amount of Discord bots our team makes it was always annoying to copy over our module loader and getting it working for that project, hence why we decided to just make one module loader which we can import into all of our Discord Bot projcets. We hope to keep this module updated and working whenever new features are added to Discord/Discord.js, but we may also add custom things to our likings. This package was created by [Bas950](https://github.com/Bas950) and [Timeraa](https://github.com/Timeraa) and is not officially endorsed by Discord nor affiliated with the company in any way.
200+
Due to the amount of Discord bots our team makes it was always annoying to copy over our module loader and getting it working for that project, hence why we decided to just make one module loader which we can import into all of our Discord Bot projcets. We hope to keep this module updated and working whenever new features are added to Discord/Discord.js, but we may also add custom things to our likings. This package was created by [Bas950](https://github.com/Bas950) and [Timeraa](https://github.com/Timeraa) and is not officially endorsed by Discord nor affiliated with the company in any way.

src/classes/ModuleLoader.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,24 @@ export default class DiscordModuleLoader {
221221
return returnEvents;
222222
}
223223

224-
async loadCommands(dir = "commands", guildId?: Snowflake) {
224+
async loadCommands(
225+
dir = "commands",
226+
guildId?: Snowflake,
227+
subDirectoryOf?: string
228+
) {
225229
dir = resolve(dir);
226230
if (!existsSync(dir)) return [];
227231

228-
const commands = (await readdir(dir)).filter(file => file.endsWith(".js")),
229-
log = this.log.extend(basename(dir));
232+
const directory = await readdir(dir, { withFileTypes: true }),
233+
commands = directory
234+
.filter(file => file.isFile() && file.name.endsWith(".js"))
235+
.map(f => f.name),
236+
subDirectories = directory
237+
.filter(file => file.isDirectory())
238+
.map(f => f.name),
239+
log = subDirectoryOf
240+
? this.log.extend(subDirectoryOf).extend(basename(dir))
241+
: this.log.extend(basename(dir));
230242

231243
log("Loading %d commands", commands.length);
232244

@@ -252,6 +264,19 @@ export default class DiscordModuleLoader {
252264
returnCommands.push([command.name.toLowerCase(), command]);
253265
log("Loaded command %s", command.name);
254266
}
267+
268+
if (!subDirectoryOf) {
269+
log("Loading %s sub-directories", subDirectories.length);
270+
for (const subDirectory of subDirectories)
271+
returnCommands.push(
272+
...(await this.loadCommands(
273+
resolve(dir, subDirectory),
274+
guildId,
275+
basename(dir)
276+
))
277+
);
278+
}
279+
255280
return returnCommands;
256281
}
257282

0 commit comments

Comments
 (0)