Discord bot to open dong files

add ping command

Vielle.dev 05c81946 1cc3c8d5

Changed files
+167 -3
src
commands
util
+3 -1
.env.template
··· 1 - TOKEN="<Your Discord Bot Token>" 1 + TOKEN="<Your Discord Bot Token>" 2 + CLIENT="app id" 3 + GUILD="dev server"
+2 -1
package.json
··· 4 4 "type": "module", 5 5 "private": true, 6 6 "scripts": { 7 - "dev": "bun --watch src/index.ts" 7 + "dev": "bun --watch src/index.ts", 8 + "commands:sync": "bun src/sync.js" 8 9 }, 9 10 "devDependencies": { 10 11 "@types/bun": "latest"
+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
··· 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
··· 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 + })();