+3
-1
.env.template
+3
-1
.env.template
+2
-1
package.json
+2
-1
package.json
+16
src/commands/util/ping.ts
+16
src/commands/util/ping.ts
···
1
+
import {
2
+
ChatInputCommandInteraction,
3
+
Client,
4
+
CommandInteraction,
5
+
SlashCommandBuilder,
6
+
} from "discord.js";
7
+
import type { customClient } from "../..";
8
+
9
+
export const data = new SlashCommandBuilder()
10
+
.setName("ping")
11
+
.setDescription("Replies with pong!");
12
+
export const execute = async (
13
+
interaction: ChatInputCommandInteraction & { client: customClient }
14
+
) => {
15
+
await interaction.reply("Pong!");
16
+
};
+92
-1
src/index.ts
+92
-1
src/index.ts
···
1
-
console.log("Hello via Bun!");
1
+
import {
2
+
ChatInputCommandInteraction,
3
+
Client,
4
+
Collection,
5
+
Events,
6
+
GatewayIntentBits,
7
+
MessageFlags,
8
+
type Interaction,
9
+
} from "discord.js";
10
+
import { Glob } from "bun";
11
+
12
+
const token = process.env.token;
13
+
if (!token) throw new Error("Token required. Please fill in TOKEN in .env");
14
+
console.log("Token Valid!");
15
+
16
+
// client typing
17
+
export type customClient = Client & {
18
+
// add command typing
19
+
commands: Collection<
20
+
string,
21
+
{
22
+
data: { name: string };
23
+
execute: (
24
+
interaction: ChatInputCommandInteraction & { client: customClient }
25
+
) => Promise<void>;
26
+
}
27
+
>;
28
+
};
29
+
// new client
30
+
const client: customClient = new Client({
31
+
intents: [GatewayIntentBits.Guilds],
32
+
// as customclient as i cannot add .commands till this is done
33
+
}) as customClient;
34
+
35
+
// setup commands
36
+
client.commands = new Collection();
37
+
const commandGlob = new Glob("**/*.ts");
38
+
for await (const file of commandGlob.scan("./src/commands")) {
39
+
const command = await import("./commands/" + file);
40
+
// check command contains all required properties
41
+
if (
42
+
"data" in command &&
43
+
"execute" in command &&
44
+
typeof command.data === "object" &&
45
+
command.data !== null &&
46
+
"name" in command.data &&
47
+
typeof command.data.name === "string"
48
+
) {
49
+
client.commands.set(command.data.name, command);
50
+
console.log("[loaded]", file);
51
+
} else {
52
+
// log missing features
53
+
console.log(`[WARNING] ${file} is not a valid command!`, command);
54
+
}
55
+
}
56
+
57
+
// when client is ready do this once
58
+
client.once(Events.ClientReady, (ready) => {
59
+
console.log(`Ready! Logged in as ${ready.user.tag}`);
60
+
});
61
+
62
+
// _interaction so we can cast types properly
63
+
// we need access to client.commands
64
+
// we could just do it as a global but this makes it go wherever the command goes soooo
65
+
client.on(Events.InteractionCreate, async (_interaction) => {
66
+
const interaction = _interaction as Interaction & { client: customClient };
67
+
if (!interaction.isChatInputCommand()) return;
68
+
const command = interaction.client.commands.get(interaction.commandName);
69
+
if (!command) {
70
+
console.error(`No command ${interaction.commandName}`);
71
+
return;
72
+
}
73
+
74
+
try {
75
+
await command.execute(interaction);
76
+
} catch (e) {
77
+
console.error(e);
78
+
if (interaction.replied || interaction.deferred) {
79
+
await interaction.followUp({
80
+
content: "There was an error while executing this command!",
81
+
flags: MessageFlags.Ephemeral,
82
+
});
83
+
} else {
84
+
await interaction.reply({
85
+
content: "There was an error while executing this command!",
86
+
flags: MessageFlags.Ephemeral,
87
+
});
88
+
}
89
+
}
90
+
});
91
+
92
+
client.login(token);
+54
src/sync.js
+54
src/sync.js
···
1
+
const { REST, Routes } = require("discord.js");
2
+
const { client: clientId, guild: guildId, token } = process.env;
3
+
const fs = require("node:fs");
4
+
const path = require("node:path");
5
+
6
+
const commands = [];
7
+
// Grab all the command folders from the commands directory you created earlier
8
+
const foldersPath = path.join(__dirname, "commands");
9
+
const commandFolders = fs.readdirSync(foldersPath);
10
+
11
+
for (const folder of commandFolders) {
12
+
// Grab all the command files from the commands directory you created earlier
13
+
const commandsPath = path.join(foldersPath, folder);
14
+
const commandFiles = fs
15
+
.readdirSync(commandsPath)
16
+
.filter((file) => file.endsWith(".ts") || file.endsWith(".js"));
17
+
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
18
+
for (const file of commandFiles) {
19
+
const filePath = path.join(commandsPath, file);
20
+
const command = require(filePath);
21
+
if ("data" in command && "execute" in command) {
22
+
commands.push(command.data.toJSON());
23
+
} else {
24
+
console.log(
25
+
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
26
+
);
27
+
}
28
+
}
29
+
}
30
+
31
+
// Construct and prepare an instance of the REST module
32
+
const rest = new REST().setToken(token);
33
+
34
+
// and deploy your commands!
35
+
(async () => {
36
+
try {
37
+
console.log(
38
+
`Started refreshing ${commands.length} application (/) commands.`
39
+
);
40
+
41
+
// The put method is used to fully refresh all commands in the guild with the current set
42
+
const data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), {
43
+
body: commands,
44
+
});
45
+
46
+
console.log(
47
+
`Successfully reloaded ${data.length} application (/) commands.`
48
+
);
49
+
} catch (error) {
50
+
// And of course, make sure you catch and log any errors!
51
+
console.error(error);
52
+
process.stdout.write(JSON.stringify(error) + "\n");
53
+
}
54
+
})();