A powerful and extendable Discord bot, with it's own module system :3 thevoid.cafe/projects/voidy

๐ŸŽ‰ Initialize Voidy discord bot

Jo f1007cfc f27d4780

+270
+1
.env.example
··· 1 + BOT_TOKEN=#Get this from https://discord.com/developers
+8
.gitignore
··· 1 + ### Jetbrains 2 + .idea 3 + 4 + ### Node/Deno/Bun 5 + node_modules 6 + 7 + ### Secrets 8 + .env
+22
README.md
··· 1 + <br> 2 + <h1 align="center">๏ธโ„๏ธ Voidy โ„๏ธ<br></h1> 3 + <div align="center">My powerful discord bot :3</div> 4 + <br> 5 + 6 + ## ๐Ÿš€ Deployment 7 + To deploy this project, in development mode, run the following command in your terminal of choice. 8 + 9 + ```sh 10 + deno task dev 11 + ``` 12 + 13 + ## ๐ŸŽจ Credits 14 + Certain features of this bot were the result 15 + of conversations with: 16 + 17 + - [Sub2Rhys](https://github.com/Sub2Rhys) 18 + - CallyHam 19 + 20 + Additionally, some of the docs and guides used: 21 + 22 + - [discordjs.guide](https://discordjs.guide)
+9
deno.json
··· 1 + { 2 + "tasks": { 3 + "dev": "deno run --watch --allow-env --allow-read --allow-net --env-file src/index.ts" 4 + }, 5 + "imports": { 6 + "@std/assert": "jsr:@std/assert@1", 7 + "discord.js": "npm:discord.js@^14.17.3" 8 + } 9 + }
+174
deno.lock
··· 1 + { 2 + "version": "4", 3 + "specifiers": { 4 + "jsr:@std/assert@1": "1.0.11", 5 + "jsr:@std/internal@^1.0.5": "1.0.5", 6 + "npm:@discordjs/builders@1.10.0": "1.10.0", 7 + "npm:discord.js@14.17.3": "14.17.3", 8 + "npm:discord.js@^14.17.3": "14.17.3" 9 + }, 10 + "jsr": { 11 + "@std/assert@1.0.11": { 12 + "integrity": "2461ef3c368fe88bc60e186e7744a93112f16fd110022e113a0849e94d1c83c1", 13 + "dependencies": [ 14 + "jsr:@std/internal" 15 + ] 16 + }, 17 + "@std/internal@1.0.5": { 18 + "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" 19 + } 20 + }, 21 + "npm": { 22 + "@discordjs/builders@1.10.0": { 23 + "integrity": "sha512-ikVZsZP+3shmVJ5S1oM+7SveUCK3L9fTyfA8aJ7uD9cNQlTqF+3Irbk2Y22KXTb3C3RNUahRkSInClJMkHrINg==", 24 + "dependencies": [ 25 + "@discordjs/formatters", 26 + "@discordjs/util", 27 + "@sapphire/shapeshift", 28 + "discord-api-types", 29 + "fast-deep-equal", 30 + "ts-mixer", 31 + "tslib" 32 + ] 33 + }, 34 + "@discordjs/collection@1.5.3": { 35 + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==" 36 + }, 37 + "@discordjs/collection@2.1.1": { 38 + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==" 39 + }, 40 + "@discordjs/formatters@0.6.0": { 41 + "integrity": "sha512-YIruKw4UILt/ivO4uISmrGq2GdMY6EkoTtD0oS0GvkJFRZbTSdPhzYiUILbJ/QslsvC9H9nTgGgnarnIl4jMfw==", 42 + "dependencies": [ 43 + "discord-api-types" 44 + ] 45 + }, 46 + "@discordjs/rest@2.4.2": { 47 + "integrity": "sha512-9bOvXYLQd5IBg/kKGuEFq3cstVxAMJ6wMxO2U3wjrgO+lHv8oNCT+BBRpuzVQh7BoXKvk/gpajceGvQUiRoJ8g==", 48 + "dependencies": [ 49 + "@discordjs/collection@2.1.1", 50 + "@discordjs/util", 51 + "@sapphire/async-queue", 52 + "@sapphire/snowflake", 53 + "@vladfrangu/async_event_emitter", 54 + "discord-api-types", 55 + "magic-bytes.js", 56 + "tslib", 57 + "undici" 58 + ] 59 + }, 60 + "@discordjs/util@1.1.1": { 61 + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==" 62 + }, 63 + "@discordjs/ws@1.2.0": { 64 + "integrity": "sha512-QH5CAFe3wHDiedbO+EI3OOiyipwWd+Q6BdoFZUw/Wf2fw5Cv2fgU/9UEtJRmJa9RecI+TAhdGPadMaEIur5yJg==", 65 + "dependencies": [ 66 + "@discordjs/collection@2.1.1", 67 + "@discordjs/rest", 68 + "@discordjs/util", 69 + "@sapphire/async-queue", 70 + "@types/ws", 71 + "@vladfrangu/async_event_emitter", 72 + "discord-api-types", 73 + "tslib", 74 + "ws" 75 + ] 76 + }, 77 + "@sapphire/async-queue@1.5.5": { 78 + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==" 79 + }, 80 + "@sapphire/shapeshift@4.0.0": { 81 + "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", 82 + "dependencies": [ 83 + "fast-deep-equal", 84 + "lodash" 85 + ] 86 + }, 87 + "@sapphire/snowflake@3.5.3": { 88 + "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==" 89 + }, 90 + "@types/node@22.5.4": { 91 + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", 92 + "dependencies": [ 93 + "undici-types" 94 + ] 95 + }, 96 + "@types/ws@8.5.14": { 97 + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", 98 + "dependencies": [ 99 + "@types/node" 100 + ] 101 + }, 102 + "@vladfrangu/async_event_emitter@2.4.6": { 103 + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==" 104 + }, 105 + "discord-api-types@0.37.118": { 106 + "integrity": "sha512-MQkHHZcytmNQ3nQOBj6a0z38swsmHiROX7hdayfd0eWVrLxaQp/6tWBZ7FO2MCKKsc+W3QWnnfOJTbtyk8C4TQ==" 107 + }, 108 + "discord.js@14.17.3": { 109 + "integrity": "sha512-8/j8udc3CU7dz3Eqch64UaSHoJtUT6IXK4da5ixjbav4NAXJicloWswD/iwn1ImZEMoAV3LscsdO0zhBh6H+0Q==", 110 + "dependencies": [ 111 + "@discordjs/builders", 112 + "@discordjs/collection@1.5.3", 113 + "@discordjs/formatters", 114 + "@discordjs/rest", 115 + "@discordjs/util", 116 + "@discordjs/ws", 117 + "@sapphire/snowflake", 118 + "discord-api-types", 119 + "fast-deep-equal", 120 + "lodash.snakecase", 121 + "tslib", 122 + "undici" 123 + ] 124 + }, 125 + "fast-deep-equal@3.1.3": { 126 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 127 + }, 128 + "lodash.snakecase@4.1.1": { 129 + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" 130 + }, 131 + "lodash@4.17.21": { 132 + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 133 + }, 134 + "magic-bytes.js@1.10.0": { 135 + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" 136 + }, 137 + "ts-mixer@6.0.4": { 138 + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" 139 + }, 140 + "tslib@2.8.1": { 141 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" 142 + }, 143 + "undici-types@6.19.8": { 144 + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" 145 + }, 146 + "undici@6.19.8": { 147 + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==" 148 + }, 149 + "ws@8.18.0": { 150 + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==" 151 + } 152 + }, 153 + "remote": { 154 + "https://deno.land/std@0.170.0/_util/asserts.ts": "d0844e9b62510f89ce1f9878b046f6a57bf88f208a10304aab50efcb48365272", 155 + "https://deno.land/std@0.170.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", 156 + "https://deno.land/std@0.170.0/fs/_util.ts": "fdc156f897197f261a1c096dcf8ff9267ed0ff42bd5b31f55053a4763a4bae3b", 157 + "https://deno.land/std@0.170.0/fs/walk.ts": "677eac2e5386217a7a4e7526769ae28b41ff4ae7a3cd0389f3aa4eb662545edd", 158 + "https://deno.land/std@0.170.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 159 + "https://deno.land/std@0.170.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 160 + "https://deno.land/std@0.170.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", 161 + "https://deno.land/std@0.170.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 162 + "https://deno.land/std@0.170.0/path/glob.ts": "81cc6c72be002cd546c7a22d1f263f82f63f37fe0035d9726aa96fc8f6e4afa1", 163 + "https://deno.land/std@0.170.0/path/mod.ts": "cf7cec7ac11b7048bb66af8ae03513e66595c279c65cfa12bfc07d9599608b78", 164 + "https://deno.land/std@0.170.0/path/posix.ts": "b859684bc4d80edfd4cad0a82371b50c716330bed51143d6dcdbe59e6278b30c", 165 + "https://deno.land/std@0.170.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 166 + "https://deno.land/std@0.170.0/path/win32.ts": "7cebd2bda6657371adc00061a1d23fdd87bcdf64b4843bb148b0b24c11b40f69" 167 + }, 168 + "workspace": { 169 + "dependencies": [ 170 + "jsr:@std/assert@1", 171 + "npm:discord.js@^14.17.3" 172 + ] 173 + } 174 + }
+11
src/commands/utility/ping.ts
··· 1 + import { SlashCommandBuilder } from "npm:@discordjs/builders@1.10.0"; 2 + import { CommandInteraction } from "npm:discord.js@14.17.3"; 3 + 4 + export default { 5 + data: new SlashCommandBuilder() 6 + .setName("ping") 7 + .setDescription("Responds with pong!"), 8 + async execute(interaction: CommandInteraction): Promise<void> { 9 + await interaction.reply({ content: "Pong ๐Ÿ“" }); 10 + } 11 + }
+17
src/handlers/commandHandler.ts
··· 1 + import { walk } from "https://deno.land/std@0.170.0/fs/walk.ts"; 2 + import { VoidyClient } from "../utils/classes/VoidyClient.ts"; 3 + 4 + export async function handleCommands(client: VoidyClient, path: string = "src/commands") { 5 + for await (const walkEntry of walk(path)) { 6 + if (!walkEntry.isFile) continue; 7 + 8 + const command = (await import(`file://${Deno.cwd()}/${walkEntry.path}`)).default; 9 + 10 + if ("data" in command && "execute" in command) { 11 + client.commands.set(command.data.name, command); 12 + console.log(`[Voidy] Loaded command: ${command.data.name}`); 13 + } else { 14 + console.log(`[Voidy] Command ${walkEntry.path} is missing the "data" or "execute" property.`); 15 + } 16 + } 17 + }
+17
src/index.ts
··· 1 + import { Events, GatewayIntentBits } from "discord.js"; 2 + import { VoidyClient } from "./utils/classes/VoidyClient.ts"; 3 + import { handleCommands } from "./handlers/commandHandler.ts"; 4 + 5 + // Create bot client instance 6 + const client = new VoidyClient({ intents: [GatewayIntentBits.Guilds] }); 7 + 8 + client.once(Events.ClientReady, (event) => { 9 + console.log(`[Voidy] Ready on account ${event.user.username}#${event.user.discriminator}`); 10 + }) 11 + 12 + // Handle commands and events 13 + await handleCommands(client, "src/commands"); 14 + // Todo: handle events 15 + 16 + // Login using the specified token 17 + client.login(Deno.env.get("BOT_TOKEN")).then(() => console.log("[Voidy] Successfully logged in!"));
+11
src/utils/classes/VoidyClient.ts
··· 1 + import { Client, ClientOptions, Collection } from "discord.js"; 2 + 3 + export class VoidyClient extends Client { 4 + constructor(options: ClientOptions) { 5 + super(options); 6 + 7 + this.commands = new Collection<string, object>(); 8 + } 9 + 10 + public commands: Collection<string, object>; 11 + }