Discord bot to open dong files

add ping command

Changed files
+167 -3
src
commands
util
+3 -1
.env.template
···
-
TOKEN="<Your Discord Bot Token>"
+
TOKEN="<Your Discord Bot Token>"
+
CLIENT="app id"
+
GUILD="dev server"
+2 -1
package.json
···
"type": "module",
"private": true,
"scripts": {
-
"dev": "bun --watch src/index.ts"
+
"dev": "bun --watch src/index.ts",
+
"commands:sync": "bun src/sync.js"
},
"devDependencies": {
"@types/bun": "latest"
+16
src/commands/util/ping.ts
···
+
import {
+
ChatInputCommandInteraction,
+
Client,
+
CommandInteraction,
+
SlashCommandBuilder,
+
} from "discord.js";
+
import type { customClient } from "../..";
+
+
export const data = new SlashCommandBuilder()
+
.setName("ping")
+
.setDescription("Replies with pong!");
+
export const execute = async (
+
interaction: ChatInputCommandInteraction & { client: customClient }
+
) => {
+
await interaction.reply("Pong!");
+
};
+92 -1
src/index.ts
···
-
console.log("Hello via Bun!");
+
import {
+
ChatInputCommandInteraction,
+
Client,
+
Collection,
+
Events,
+
GatewayIntentBits,
+
MessageFlags,
+
type Interaction,
+
} from "discord.js";
+
import { Glob } from "bun";
+
+
const token = process.env.token;
+
if (!token) throw new Error("Token required. Please fill in TOKEN in .env");
+
console.log("Token Valid!");
+
+
// client typing
+
export type customClient = Client & {
+
// add command typing
+
commands: Collection<
+
string,
+
{
+
data: { name: string };
+
execute: (
+
interaction: ChatInputCommandInteraction & { client: customClient }
+
) => Promise<void>;
+
}
+
>;
+
};
+
// new client
+
const client: customClient = new Client({
+
intents: [GatewayIntentBits.Guilds],
+
// as customclient as i cannot add .commands till this is done
+
}) as customClient;
+
+
// setup commands
+
client.commands = new Collection();
+
const commandGlob = new Glob("**/*.ts");
+
for await (const file of commandGlob.scan("./src/commands")) {
+
const command = await import("./commands/" + file);
+
// check command contains all required properties
+
if (
+
"data" in command &&
+
"execute" in command &&
+
typeof command.data === "object" &&
+
command.data !== null &&
+
"name" in command.data &&
+
typeof command.data.name === "string"
+
) {
+
client.commands.set(command.data.name, command);
+
console.log("[loaded]", file);
+
} else {
+
// log missing features
+
console.log(`[WARNING] ${file} is not a valid command!`, command);
+
}
+
}
+
+
// when client is ready do this once
+
client.once(Events.ClientReady, (ready) => {
+
console.log(`Ready! Logged in as ${ready.user.tag}`);
+
});
+
+
// _interaction so we can cast types properly
+
// we need access to client.commands
+
// we could just do it as a global but this makes it go wherever the command goes soooo
+
client.on(Events.InteractionCreate, async (_interaction) => {
+
const interaction = _interaction as Interaction & { client: customClient };
+
if (!interaction.isChatInputCommand()) return;
+
const command = interaction.client.commands.get(interaction.commandName);
+
if (!command) {
+
console.error(`No command ${interaction.commandName}`);
+
return;
+
}
+
+
try {
+
await command.execute(interaction);
+
} catch (e) {
+
console.error(e);
+
if (interaction.replied || interaction.deferred) {
+
await interaction.followUp({
+
content: "There was an error while executing this command!",
+
flags: MessageFlags.Ephemeral,
+
});
+
} else {
+
await interaction.reply({
+
content: "There was an error while executing this command!",
+
flags: MessageFlags.Ephemeral,
+
});
+
}
+
}
+
});
+
+
client.login(token);
+54
src/sync.js
···
+
const { REST, Routes } = require("discord.js");
+
const { client: clientId, guild: guildId, token } = process.env;
+
const fs = require("node:fs");
+
const path = require("node:path");
+
+
const commands = [];
+
// Grab all the command folders from the commands directory you created earlier
+
const foldersPath = path.join(__dirname, "commands");
+
const commandFolders = fs.readdirSync(foldersPath);
+
+
for (const folder of commandFolders) {
+
// Grab all the command files from the commands directory you created earlier
+
const commandsPath = path.join(foldersPath, folder);
+
const commandFiles = fs
+
.readdirSync(commandsPath)
+
.filter((file) => file.endsWith(".ts") || file.endsWith(".js"));
+
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
+
for (const file of commandFiles) {
+
const filePath = path.join(commandsPath, file);
+
const command = require(filePath);
+
if ("data" in command && "execute" in command) {
+
commands.push(command.data.toJSON());
+
} else {
+
console.log(
+
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
+
);
+
}
+
}
+
}
+
+
// Construct and prepare an instance of the REST module
+
const rest = new REST().setToken(token);
+
+
// and deploy your commands!
+
(async () => {
+
try {
+
console.log(
+
`Started refreshing ${commands.length} application (/) commands.`
+
);
+
+
// The put method is used to fully refresh all commands in the guild with the current set
+
const data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), {
+
body: commands,
+
});
+
+
console.log(
+
`Successfully reloaded ${data.length} application (/) commands.`
+
);
+
} catch (error) {
+
// And of course, make sure you catch and log any errors!
+
console.error(error);
+
process.stdout.write(JSON.stringify(error) + "\n");
+
}
+
})();