Skip to content

Commit a3d2054

Browse files
authored
Add commands structure (#10)
* Create commands structure * Create hello command * Register application commands * Refactor file
1 parent 31ea8cc commit a3d2054

File tree

4 files changed

+97
-6
lines changed

4 files changed

+97
-6
lines changed

src/commands/commands.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { SlashCommandBuilder } from '@discordjs/builders';
2+
import { Client, CommandInteraction } from 'discord.js';
3+
import fs from 'fs';
4+
import path from 'path';
5+
6+
export type AppCommands = {
7+
hello?: AppCommand;
8+
};
9+
export type AppCommand = {
10+
data: SlashCommandBuilder;
11+
execute: (data: AppCommandOptions) => void;
12+
};
13+
export type AppCommandOptions = {
14+
interaction: CommandInteraction;
15+
app: Client;
16+
};
17+
type ExportedAppCommand = {
18+
default: AppCommand;
19+
};
20+
export function getApplicationCommands(): AppCommands {
21+
const appCommands: AppCommands = {};
22+
const loadModules = (directoryPath: string) => {
23+
const files = fs.readdirSync(directoryPath, { withFileTypes: true });
24+
files.forEach((file) => {
25+
const filePath = path.join(directoryPath, file.name);
26+
if (file.isDirectory()) {
27+
return loadModules(filePath);
28+
}
29+
if (file.name === 'index.js') {
30+
const modulePath = `./${filePath.replace('dist/commands/', '')}`;
31+
const currentModule = require(modulePath) as ExportedAppCommand;
32+
appCommands[directoryPath.replace('dist/commands/', '') as keyof AppCommands] =
33+
currentModule.default;
34+
}
35+
});
36+
};
37+
loadModules('./dist/commands');
38+
return appCommands;
39+
}

src/commands/hello/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { SlashCommandBuilder } from 'discord.js';
2+
import { AppCommand, AppCommandOptions } from '../commands';
3+
4+
export default {
5+
data: new SlashCommandBuilder().setName('hello').setDescription('Greets the user!'),
6+
async execute({ interaction }: AppCommandOptions) {
7+
try {
8+
await interaction.deferReply();
9+
await interaction.editReply('Hello!');
10+
} catch (error) {
11+
console.log(error);
12+
}
13+
},
14+
} as AppCommand;

src/events/events.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Client } from 'discord.js';
22
import fs from 'fs';
33
import path from 'path';
4+
import { AppCommands, getApplicationCommands } from '../commands/commands';
5+
6+
const appCommands = getApplicationCommands();
47

58
interface Props {
69
app: Client;
@@ -10,6 +13,7 @@ type ExportedEventModule = {
1013
};
1114
export type EventModule = {
1215
app: Client;
16+
appCommands?: AppCommands;
1317
};
1418
export function registerEventHandlers({ app }: Props): void {
1519
const loadModules = (directoryPath: string) => {
@@ -27,7 +31,7 @@ export function registerEventHandlers({ app }: Props): void {
2731
if (file.name === 'index.js') {
2832
const modulePath = `.${filePath.replace('dist/events', '')}`;
2933
const currentModule = require(modulePath) as ExportedEventModule;
30-
currentModule.default({ app });
34+
currentModule.default({ app, appCommands });
3135
}
3236
});
3337
});

src/events/ready/index.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,45 @@
1-
import { Client } from 'discord.js';
1+
import { REST, Routes } from 'discord.js';
2+
import { AppCommands } from '../../commands/commands';
3+
import { BOT_TOKEN, ENV, GUILD_IDS } from '../../config/environment';
4+
import { EventModule } from '../events';
25

3-
interface Props {
4-
app: Client;
5-
}
6-
export default function ({ app }: Props) {
6+
const rest = new REST({ version: '9' }).setToken(BOT_TOKEN);
7+
8+
const registerApplicationCommands = async (commands?: AppCommands) => {
9+
if (!commands) return;
10+
const commandList = Object.keys(commands)
11+
.map((key) => {
12+
const commandKey = commands[key as keyof AppCommands];
13+
if (commandKey) return commandKey.data;
14+
})
15+
.map((command) => command && command.toJSON());
16+
17+
try {
18+
if (ENV === 'dev') {
19+
if (GUILD_IDS) {
20+
//Registering guild-only commands to the bot; I like to use a different bot for dev purposes
21+
//TODO: Change id here to placeholder
22+
await rest.put(Routes.applicationGuildCommands('929421200797626388', GUILD_IDS), {
23+
body: commandList,
24+
});
25+
console.log('Successfully registered guild application commands');
26+
}
27+
} else {
28+
//Registering global commands for the bot i.e. every guild will see the commands; I mostly just use this in production
29+
//TODO: Change id here to placeholder
30+
await rest.put(Routes.applicationCommands('518196430104428579'), { body: commandList });
31+
console.log('Successfully registered global application commands');
32+
}
33+
} catch (error) {
34+
//TODO: Add error handling
35+
console.log(error);
36+
}
37+
};
38+
39+
export default function ({ app, appCommands }: EventModule) {
740
app.once('ready', async () => {
841
try {
42+
await registerApplicationCommands(appCommands);
943
console.log("I'm booting up! (◕ᴗ◕✿)");
1044
} catch (error) {
1145
console.log(error);

0 commit comments

Comments
 (0)