diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..22ebc2a
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,2 @@
+DISCORD_TOKEN=a
+CLIENT_ID=b
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..16208e3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+.vscode/
+.env
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d323bfc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 isabelroses
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6eb1e31
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# Blahaj
+
+
+
+ BlÄhaj
+
+
+Blahaj is a simple discord bot that is designed to do anything that you want it to do. It is written in JavaScript and uses the discord.js library.
+
+## Installation
+
+To install blahaj, you need to have node.js installed. You can download it from [here](https://nodejs.org/en/download/). Once you have node.js installed, you can install blahaj by running the following command:
+
+Then navigate to the file and run the following command:
+```bash
+npm install
+```
+This will install all the dependencies that blahaj needs to run.
+
+## Usage
+
+To run blahaj
+```bash
+node src/index.js
+```
+
+
+## Thanks
+
+Thanks to [discord.js](https://discord.js.org/#/) for making this bot possible.
+
+Thanks to this [reddit post](https://www.reddit.com/r/BLAHAJ/comments/s91n8d/some_blahaj_emojis/) for the emojis.
diff --git a/assets/BigBlobhajHug.svg b/assets/BigBlobhajHug.svg
new file mode 100644
index 0000000..61aa622
--- /dev/null
+++ b/assets/BigBlobhajHug.svg
@@ -0,0 +1,6696 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..835e693
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,11 @@
+{buildNpmPackage}:
+buildNpmPackage {
+ pname = "blahaj";
+ version = "0.1.0";
+
+ src = ./.;
+
+ dontNpmBuild = true;
+
+ npmDepsHash = "sha256-mRmu2UIJTWj4d/UypUAM4+3Q8cbuVpazMuv4b21Yxho=";
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..bd4037a
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,26 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1710795924,
+ "narHash": "sha256-Cy5wSZkv1Ts0CD6Tk1mXMNE/Aa0+MGsKTRDak16qcoE=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "b081342f1c16e4cbe4f40f139bbdda1475ea306a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..41d3e52
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,26 @@
+{
+ description = "Blahaj";
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs";
+ };
+
+ outputs = {
+ self,
+ nixpkgs,
+ }: let
+ systems = ["x86_64-linux" "aarch64-linux"];
+ forEachSystem = nixpkgs.lib.genAttrs systems;
+
+ pkgsForEach = nixpkgs.legacyPackages;
+ in {
+ packages = forEachSystem (system: {
+ default = pkgsForEach.${system}.callPackage ./default.nix {};
+ });
+
+ devShells = forEachSystem (system: {
+ default = pkgsForEach.${system}.callPackage ./shell.nix {};
+ });
+
+ nixosModules.default = import ./module.nix self;
+ };
+}
diff --git a/module.nix b/module.nix
new file mode 100644
index 0000000..cb62730
--- /dev/null
+++ b/module.nix
@@ -0,0 +1,26 @@
+self: {
+ pkgs,
+ config,
+ lib,
+ ...
+}: let
+ inherit (lib) mkIf mkEnableOption;
+in {
+ options.services.blahaj.enable = mkEnableOption "blahaj";
+
+ config = mkIf config.services.blahaj.enable {
+ systemd.services."blahaj" = {
+ description = "blahaj";
+ after = ["network.target"];
+ wantedBy = ["multi-user.target"];
+ path = [pkgs.nodejs];
+
+ serviceConfig = {
+ Type = "simple";
+ DynamicUser = true;
+ ExecStart = "node ${self.packages.${pkgs.stdenv.hostPlatform.system}.default}/lib/node_modules/blahaj";
+ Restart = "always";
+ };
+ };
+ };
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..db2a7a1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,612 @@
+{
+ "name": "blowhigh",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "blowhigh",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@discordjs/rest": "^1.6.0",
+ "chalk": "^4.1.2",
+ "discord-api-types": "^0.37.36",
+ "discord.js": "^14.8.0",
+ "dotenv": "^16.0.3"
+ }
+ },
+ "node_modules/@discordjs/builders": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz",
+ "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==",
+ "dependencies": {
+ "@discordjs/formatters": "^0.3.3",
+ "@discordjs/util": "^1.0.2",
+ "@sapphire/shapeshift": "^3.9.3",
+ "discord-api-types": "0.37.61",
+ "fast-deep-equal": "^3.1.3",
+ "ts-mixer": "^6.0.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/builders/node_modules/@discordjs/util": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz",
+ "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/builders/node_modules/discord-api-types": {
+ "version": "0.37.61",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
+ "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw=="
+ },
+ "node_modules/@discordjs/collection": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
+ "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/formatters": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz",
+ "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==",
+ "dependencies": {
+ "discord-api-types": "0.37.61"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/formatters/node_modules/discord-api-types": {
+ "version": "0.37.61",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
+ "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw=="
+ },
+ "node_modules/@discordjs/rest": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.1.tgz",
+ "integrity": "sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ==",
+ "dependencies": {
+ "@discordjs/collection": "^1.5.1",
+ "@discordjs/util": "^0.3.0",
+ "@sapphire/async-queue": "^1.5.0",
+ "@sapphire/snowflake": "^3.4.2",
+ "discord-api-types": "^0.37.41",
+ "file-type": "^18.3.0",
+ "tslib": "^2.5.0",
+ "undici": "^5.22.0"
+ },
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/@discordjs/util": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.3.1.tgz",
+ "integrity": "sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/@discordjs/ws": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz",
+ "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==",
+ "dependencies": {
+ "@discordjs/collection": "^2.0.0",
+ "@discordjs/rest": "^2.1.0",
+ "@discordjs/util": "^1.0.2",
+ "@sapphire/async-queue": "^1.5.0",
+ "@types/ws": "^8.5.9",
+ "@vladfrangu/async_event_emitter": "^2.2.2",
+ "discord-api-types": "0.37.61",
+ "tslib": "^2.6.2",
+ "ws": "^8.14.2"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz",
+ "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@discordjs/ws/node_modules/@discordjs/rest": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz",
+ "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==",
+ "dependencies": {
+ "@discordjs/collection": "^2.0.0",
+ "@discordjs/util": "^1.0.2",
+ "@sapphire/async-queue": "^1.5.0",
+ "@sapphire/snowflake": "^3.5.1",
+ "@vladfrangu/async_event_emitter": "^2.2.2",
+ "discord-api-types": "0.37.61",
+ "magic-bytes.js": "^1.5.0",
+ "tslib": "^2.6.2",
+ "undici": "5.27.2"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/ws/node_modules/@discordjs/util": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz",
+ "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/@discordjs/ws/node_modules/discord-api-types": {
+ "version": "0.37.61",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
+ "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw=="
+ },
+ "node_modules/@discordjs/ws/node_modules/undici": {
+ "version": "5.27.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz",
+ "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==",
+ "dependencies": {
+ "@fastify/busboy": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
+ "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@sapphire/async-queue": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz",
+ "integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/shapeshift": {
+ "version": "3.9.6",
+ "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.6.tgz",
+ "integrity": "sha512-4+Na/fxu2SEepZRb9z0dbsVh59QtwPuBg/UVaDib3av7ZY14b14+z09z6QVn0P6Dv6eOU2NDTsjIi0mbtgP56g==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@sapphire/snowflake": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
+ "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+ },
+ "node_modules/@types/node": {
+ "version": "20.11.29",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.29.tgz",
+ "integrity": "sha512-P99thMkD/1YkCvAtOd6/zGedKNA0p2fj4ZpjCzcNiSCBWgm3cNRTBfa/qjFnsKkkojxu4vVLtWpesnZ9+ap+gA==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.9",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz",
+ "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vladfrangu/async_event_emitter": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz",
+ "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/discord-api-types": {
+ "version": "0.37.75",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.75.tgz",
+ "integrity": "sha512-pnb2iPp63+TqSs+3MCzl47y0CFQYjDDQBmuiCs0LkLoRdFx2dDpjrzJYC7BcHJCa/Xlpf+/JuDT/YADvQLu3yQ=="
+ },
+ "node_modules/discord.js": {
+ "version": "14.14.1",
+ "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz",
+ "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==",
+ "dependencies": {
+ "@discordjs/builders": "^1.7.0",
+ "@discordjs/collection": "1.5.3",
+ "@discordjs/formatters": "^0.3.3",
+ "@discordjs/rest": "^2.1.0",
+ "@discordjs/util": "^1.0.2",
+ "@discordjs/ws": "^1.0.2",
+ "@sapphire/snowflake": "3.5.1",
+ "@types/ws": "8.5.9",
+ "discord-api-types": "0.37.61",
+ "fast-deep-equal": "3.1.3",
+ "lodash.snakecase": "4.1.1",
+ "tslib": "2.6.2",
+ "undici": "5.27.2",
+ "ws": "8.14.2"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/@discordjs/rest": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz",
+ "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==",
+ "dependencies": {
+ "@discordjs/collection": "^2.0.0",
+ "@discordjs/util": "^1.0.2",
+ "@sapphire/async-queue": "^1.5.0",
+ "@sapphire/snowflake": "^3.5.1",
+ "@vladfrangu/async_event_emitter": "^2.2.2",
+ "discord-api-types": "0.37.61",
+ "magic-bytes.js": "^1.5.0",
+ "tslib": "^2.6.2",
+ "undici": "5.27.2"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz",
+ "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/discord.js/node_modules/@discordjs/util": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz",
+ "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==",
+ "engines": {
+ "node": ">=16.11.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/@sapphire/snowflake": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz",
+ "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/discord-api-types": {
+ "version": "0.37.61",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz",
+ "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw=="
+ },
+ "node_modules/discord.js/node_modules/undici": {
+ "version": "5.27.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz",
+ "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==",
+ "dependencies": {
+ "@fastify/busboy": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/file-type": {
+ "version": "18.7.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz",
+ "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==",
+ "dependencies": {
+ "readable-web-to-node-stream": "^3.0.2",
+ "strtok3": "^7.0.0",
+ "token-types": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
+ },
+ "node_modules/magic-bytes.js": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
+ "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="
+ },
+ "node_modules/peek-readable": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz",
+ "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readable-web-to-node-stream": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+ "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+ "dependencies": {
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strtok3": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz",
+ "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0",
+ "peek-readable": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/token-types": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz",
+ "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0",
+ "ieee754": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/ts-mixer": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
+ "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
+ "node_modules/undici": {
+ "version": "5.28.3",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
+ "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
+ "dependencies": {
+ "@fastify/busboy": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/ws": {
+ "version": "8.14.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
+ "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5b268f9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,18 @@
+{
+ "dependencies": {
+ "@discordjs/rest": "^1.6.0",
+ "chalk": "^4.1.2",
+ "discord-api-types": "^0.37.36",
+ "discord.js": "^14.8.0",
+ "dotenv": "^16.0.3"
+ },
+ "name": "blahaj",
+ "version": "1.0.0",
+ "main": "src/bot.js",
+ "scripts": {
+ "start": "node ."
+ },
+ "author": "isabelroses",
+ "license": "MIT",
+ "description": "A silly little discord bot"
+}
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..82f6383
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,24 @@
+{
+ eslint_d,
+ prettierd,
+ callPackage,
+ writeShellScriptBin,
+}: let
+ mainPkg = callPackage ./default.nix {};
+ mkNpxAlias = name: writeShellScriptBin name "npx ${name} \"$@\"";
+in
+ mainPkg.overrideAttrs (oa: {
+ nativeBuildInputs =
+ [
+ eslint_d
+ prettierd
+ (mkNpxAlias "tsc")
+ (mkNpxAlias "tsserver")
+ ]
+ ++ (oa.nativeBuildInputs or []);
+
+ shellHook = ''
+ eslint_d start # start eslint daemon
+ eslint_d status # inform user about eslint daemon status
+ '';
+ })
diff --git a/src/bot.js b/src/bot.js
new file mode 100644
index 0000000..2eaa8d7
--- /dev/null
+++ b/src/bot.js
@@ -0,0 +1,19 @@
+require('dotenv').config();
+const { Client, Collection, GatewayIntentBits } = require('discord.js');
+const fs = require('fs');
+
+
+const client = new Client({ intents: GatewayIntentBits.Guilds });
+client.commands = new Collection();
+client.commandArray = [];
+client.buttons = new Collection();
+
+const functionFolders = fs.readdirSync('./src/functions');
+for (const folder of functionFolders) {
+ const functionFiles = fs.readdirSync(`./src/functions/${folder}`).filter(file => file.endsWith('.js'));
+ for (const file of functionFiles) require(`./functions/${folder}/${file}`)(client);
+}
+
+client.handleComponents();
+client.handleEvents();
+client.login(process.env.DISCORD_TOKEN);
diff --git a/src/commands/help/help.js b/src/commands/help/help.js
new file mode 100644
index 0000000..ef46554
--- /dev/null
+++ b/src/commands/help/help.js
@@ -0,0 +1,99 @@
+const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('help')
+ .setDescription('Replies with Help!'),
+ async execute(interaction, client) {
+ const embed = new EmbedBuilder()
+ .setTitle('Help')
+ .setColor([255, 255, 255])
+ .setThumbnail(client.user.displayAvatarURL({ dynamic: true }))
+ .setDescription('This is a list of all the commands available to you.')
+ .addFields({ name: 'Page 1', value: 'Help & Resources' })
+ .addFields({ name: 'Page 2', value: 'Tools' })
+ .addFields({ name: 'Page 3', value: 'Fun' })
+ .addFields({ name: 'Page 4', value: 'Moderattion' })
+
+ const embed2 = new EmbedBuilder()
+ .setTitle('Help')
+ .setColor([255, 255, 255])
+ .setThumbnail(client.user.displayAvatarURL({ dynamic: true }))
+ .setDescription('This is a list of all the commands available to you.')
+ .addFields({ name: '/help', value: 'Do /help for this page' })
+ .addFields({ name: '/ping', value: 'Do /ping to get the bot\'s ping' })
+ .addFields({ name: '/invite', value: 'Do /invite to get the bot\'s invite link' })
+
+ const embed3 = new EmbedBuilder()
+ .setTitle('Help')
+ .setColor([255, 255, 255])
+ .setThumbnail(client.user.displayAvatarURL({ dynamic: true }))
+ .setDescription('This is a list of all the commands available to you.')
+ .addFields({ name: '/whois', value: 'Do /whois to get a the user information of a given user' })
+ .addFields({ name: '/avatar', value: 'Do /avatar to get a the avatar of a given user' })
+ .addFields({ name: '/serverinfo', value: 'Do /serverinfo to get the server information' })
+ .addFields({ name: '/botinfo', value: 'Do /botinfo to get the bot information' })
+ .addFields({ name: '/embed', value: 'Do /emebed to help you make a embed' })
+
+ const embed4 = new EmbedBuilder()
+ .setTitle('Help')
+ .setColor([255, 255, 255])
+ .setThumbnail(client.user.displayAvatarURL({ dynamic: true }))
+ .setDescription('This is a list of all the commands available to you.')
+ .addFields({ name: '/kick', value: 'Do /kick to kick a user' })
+ .addFields({ name: '/ban', value: 'Do /ban to ban a user' })
+ .addFields({ name: '/timeout', value: 'Do /timeout to timeout a user' })
+ .addFields({ name: '/untimeout', value: 'Do /untimeout to untimeout a user' })
+ .addFields({ name: '/clear', value: 'Do /clear to clear a given amount of messages' })
+
+ const buttons = new ActionRowBuilder()
+ .addComponents(
+ new ButtonBuilder()
+ .setLabel('Page 1')
+ .setStyle('Primary')
+ .setCustomId('page1'),
+ new ButtonBuilder()
+ .setLabel('Page 2')
+ .setStyle('Primary')
+ .setCustomId('page2'),
+ new ButtonBuilder()
+ .setLabel('Page 3')
+ .setStyle('Primary')
+ .setCustomId('page3'),
+ new ButtonBuilder()
+ .setLabel('Page 4')
+ .setStyle('Primary')
+ .setCustomId('page4')
+ )
+
+ const message = await interaction.reply({ embeds: [embed], components: [buttons] });
+ const collector = await message.createMessageComponentCollector();
+
+ collector.on('collect', async i => {
+ if (i.customId === `page1`) {
+ if (i.user.id !== interaction.user.id) {
+ return await i.update({ content: `Only ${interaction.user.tag} can use these buttons!`, ephemeral: true });
+ }
+ await i.update({ embeds: [embed], components: [buttons] });
+ }
+ if (i.customId === `page2`) {
+ if (i.user.id !== interaction.user.id) {
+ return await i.update({ content: `Only ${interaction.user.tag} can use these buttons!`, ephemeral: true });
+ }
+ await i.update({ embeds: [embed2], components: [buttons] });
+ }
+ if (i.customId === `page3`) {
+ if (i.user.id !== interaction.user.id) {
+ return await i.update({ content: `Only ${interaction.user.tag} can use these buttons!`, ephemeral: true });
+ }
+ await i.update({ embeds: [embed3], components: [buttons] });
+ }
+ if (i.customId === `page4`) {
+ if (i.user.id !== interaction.user.id) {
+ return await i.update({ content: `Only ${interaction.user.tag} can use these buttons!`, ephemeral: true });
+ }
+ await i.update({ embeds: [embed4], components: [buttons] });
+ }
+ });
+ }
+};
diff --git a/src/commands/help/invite.js b/src/commands/help/invite.js
new file mode 100644
index 0000000..6d7e6a9
--- /dev/null
+++ b/src/commands/help/invite.js
@@ -0,0 +1,10 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('invite')
+ .setDescription('Replies with the bot\'s invite link!'),
+ async execute(interaction) {
+ await interaction.reply({ content: 'https://discord.com/api/oauth2/authorize?client_id=1087418361283092510&permissions=8&scope=bot%20applications.commands' });
+ },
+};
\ No newline at end of file
diff --git a/src/commands/help/ping.js b/src/commands/help/ping.js
new file mode 100644
index 0000000..80baac1
--- /dev/null
+++ b/src/commands/help/ping.js
@@ -0,0 +1,17 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('ping')
+ .setDescription('Replies with Pong!'),
+ async execute(interaction, client) {
+ const msg = await interaction.deferReply({
+ fetchReply: true
+ });
+
+ const newMessage = `API Latency is ${Math.round(client.ws.ping)}ms\nMessage Latency is ${msg.createdTimestamp - interaction.createdTimestamp}ms`;
+ await interaction.editReply({
+ content: newMessage
+ });
+ }
+};
\ No newline at end of file
diff --git a/src/commands/moderation/ban.js b/src/commands/moderation/ban.js
new file mode 100644
index 0000000..b592fb9
--- /dev/null
+++ b/src/commands/moderation/ban.js
@@ -0,0 +1,32 @@
+const { SlashCommandBuilder, PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('ban')
+ .setDescription('Bans a user')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers)
+ .addUserOption(option => option.setName('target').setDescription('The user to ban').setRequired(true))
+ .addStringOption(option => option.setName('reason').setDescription('The reason for the ban').setRequired(false)),
+ async execute(interaction) {
+ const user = interaction.options.getUser('target');
+ const member = await interaction.guild.members.fetch(user.id).catch(console.error);
+ let reason = interaction.options.getString('reason');
+ if (!reason) reason = 'No reason provided';
+
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.BanMembers)) return await interaction.reply({ content: 'You do not have permission to ban this user', ephemeral: true })
+ if (!member.kickable) return await interaction.reply({ content: 'This user cannot be banned', ephemeral: true })
+ if (!member) return await interaction.reply({ content: `User ${user.tag} is not in this server`, ephemeral: true })
+ if (interaction.member.id === user.id) return await interaction.reply({ content: 'You cannot ban yourself', ephemeral: true })
+ if (member.permissions.has(PermissionsBitField.Flags.Administrator)) return await interaction.reply({ content: 'You cannot ban this user', ephemeral: true })
+
+ user.send(`You have been banned from ${interaction.guild.name} for ${reason}`).catch(console.log("Dm's are disabled for this user"));
+ await member.ban({
+ deleteMessageSeconds: 60 * 60 * 24 * 7,
+ reason: reason,
+ }).catch(console.error);
+ await interaction.reply({
+ content: `Banned ${user.tag} for ${reason}`,
+ ephemeral: true
+ })
+ }
+};
diff --git a/src/commands/moderation/kick.js b/src/commands/moderation/kick.js
new file mode 100644
index 0000000..1f1c886
--- /dev/null
+++ b/src/commands/moderation/kick.js
@@ -0,0 +1,29 @@
+const { SlashCommandBuilder, PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('kick')
+ .setDescription('Kicks a user')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers)
+ .addUserOption(option => option.setName('target').setDescription('The user to kick').setRequired(true))
+ .addStringOption(option => option.setName('reason').setDescription('The reason for the kick').setRequired(false)),
+ async execute(interaction) {
+ const user = interaction.options.getUser('target');
+ const member = await interaction.guild.members.fetch(user.id).catch(console.error);
+ let reason = interaction.options.getString('reason');
+ if (!reason) reason = 'No reason provided';
+
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.KickMembers)) return await interaction.reply({ content: 'You do not have permission to kick this user', ephemeral: true })
+ if (!member.kickable) return await interaction.reply({ content: 'This user cannot be kicked', ephemeral: true })
+ if (!member) return await interaction.reply({ content: `User ${user.tag} is not in this server`, ephemeral: true })
+ if (interaction.member.id === user.id) return await interaction.reply({ content: 'You cannot kick yourself', ephemeral: true })
+ if (member.permissions.has(PermissionsBitField.Flags.Administrator)) return await interaction.reply({ content: 'You cannot kick this user', ephemeral: true })
+
+ user.send(`You have been kicked from ${interaction.guild.name} for ${reason}`).catch(console.error);
+ await member.kick(`You have been kicked from ${interaction.guild.name} for ${reason}`).catch(console.log("Dm's are disabled for this user"));
+ await interaction.reply({
+ content: `Kicked ${user.tag} for ${reason}`,
+ ephemeral: true
+ })
+ }
+};
diff --git a/src/commands/moderation/purge.js b/src/commands/moderation/purge.js
new file mode 100644
index 0000000..1ff1327
--- /dev/null
+++ b/src/commands/moderation/purge.js
@@ -0,0 +1,27 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+const { PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('purge')
+ .setDescription('Deletes messages.')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages)
+ .addIntegerOption(option =>
+ option.setName('amount')
+ .setMinValue(1)
+ .setMaxValue(100)
+ .setDescription('The amount of messages to delete.')
+ .setRequired(true)),
+ async execute(interaction) {
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.ManageMessages)) return await interaction.reply({ content: 'You do not have permission to purge messages', ephemeral: true })
+
+ let amount = interaction.options.getInteger('amount');
+
+ await interaction.channel.bulkDelete(amount).catch(err => {
+ console.error(err);
+ interaction.reply({ content: 'There was an error trying to purge messages in this channel!', ephemeral: true });
+ });
+
+ await interaction.reply({ content: `Successfully deleted ${amount} messages.`, ephemeral: true });
+ }
+};
\ No newline at end of file
diff --git a/src/commands/moderation/timeout.js b/src/commands/moderation/timeout.js
new file mode 100644
index 0000000..5f8144b
--- /dev/null
+++ b/src/commands/moderation/timeout.js
@@ -0,0 +1,41 @@
+const { SlashCommandBuilder, PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('timeout')
+ .setDescription('Times out a user')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
+ .addUserOption(option => option.setName('target').setDescription('The user to time out').setRequired(true))
+ .addStringOption(option => option.setName('time').setDescription('The duration to time the user out').setRequired(true).addChoices(
+ { name: '60 seconds', value: '60' },
+ { name: '5 minutes', value: '300' },
+ { name: '10 minutes', value: '600' },
+ { name: '30 minutes', value: '1800' },
+ { name: '1 hour', value: '3600' },
+ { name: '12 hours', value: '43200' },
+ { name: '1 day', value: '86400' },
+ { name: '1 week', value: '604800' },
+ { name: '1 month', value: '2629743' }
+ ))
+ .addStringOption(option => option.setName('reason').setDescription('The reason for the timeout').setRequired(false)),
+ async execute(interaction) {
+ const user = interaction.options.getUser('target');
+ const member = await interaction.guild.members.fetch(user.id).catch(console.error);
+ let reason = interaction.options.getString('reason');
+ let time = interaction.options.getString('time');
+ if (!time) time = '60';
+ if (!reason) reason = 'No reason provided';
+
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers)) return await interaction.reply({ content: 'You do not have permission to timeout this user', ephemeral: true })
+ if (!member.kickable) return await interaction.reply({ content: 'This user cannot be timed out', ephemeral: true })
+ if (!member) return await interaction.reply({ content: `User ${user.tag} is not in this server`, ephemeral: true })
+ if (interaction.member.id === user.id) return await interaction.reply({ content: 'You cannot timeout yourself', ephemeral: true })
+ if (member.permissions.has(PermissionsBitField.Flags.Administrator)) return await interaction.reply({ content: 'You cannot timeout this user', ephemeral: true })
+
+ await member.timeout(time * 1000, reason).catch(console.error);
+ await interaction.reply({
+ content: `Timed out ${user.tag} for ${time} seconds for ${reason}`,
+ ephemeral: true
+ })
+ }
+};
diff --git a/src/commands/moderation/unban.js b/src/commands/moderation/unban.js
new file mode 100644
index 0000000..2f08fea
--- /dev/null
+++ b/src/commands/moderation/unban.js
@@ -0,0 +1,28 @@
+const { SlashCommandBuilder, PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('unban')
+ .setDescription('Unbans a user')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers)
+ .addUserOption(option => option.setName('target').setDescription('The user to unban').setRequired(true)),
+ async execute(interaction, client) {
+ const userid = interaction.options.getUser('target');
+
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.BanMembers)) return await interaction.reply({ content: 'You do not have permission to unbanned this user', ephemeral: true })
+ if (!member.kickable) return await interaction.reply({ content: 'This user cannot be unbanned out', ephemeral: true })
+ if (!member) return await interaction.reply({ content: `User ${user.tag} is not in this server`, ephemeral: true })
+ if (interaction.member.id === userid) return await interaction.reply({ content: 'You cannot unbanned yourself', ephemeral: true })
+ if (member.permissions.has(PermissionsBitField.Flags.Administrator)) return await interaction.reply({ content: 'You cannot unbanned this user', ephemeral: true })
+
+ await interactions.guild.ban.fetch().then(async bans => {
+ if (bans.size == 0) return await interaction.reply({ content: 'There are no banned users in this server', ephemeral: true });
+ let bUser = bans.find(b => b.user.id == userid);
+ if (!bUser) return await interaction.reply({ content: 'This user is not banned', ephemeral: true });
+
+ await interaction.guild.bans.remove(userid).catch(err => {
+ return interaction.reply({ content: 'There was an error unbanning this user', ephemeral: true });
+ });
+ });
+ }
+};
diff --git a/src/commands/moderation/untimeout.js b/src/commands/moderation/untimeout.js
new file mode 100644
index 0000000..fd96fdd
--- /dev/null
+++ b/src/commands/moderation/untimeout.js
@@ -0,0 +1,25 @@
+const { SlashCommandBuilder, PermissionsBitField } = require('discord.js');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('untimeout')
+ .setDescription('Untimes out a user')
+ .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
+ .addUserOption(option => option.setName('target').setDescription('The user to untimeout').setRequired(true)),
+ async execute(interaction) {
+ const user = interaction.options.getUser('target');
+ const member = await interaction.guild.members.fetch(user.id).catch(console.error);
+
+ if (!interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers)) return await interaction.reply({ content: 'You do not have permission to untimeout this user', ephemeral: true })
+ if (!member.isTimeout) return await interaction.reply({ content: `User ${user.tag} is not timed out`, ephemeral: true })
+ if (!member) return await interaction.reply({ content: `User ${user.tag} is not in this server`, ephemeral: true })
+ if (interaction.member.id === user.id) return await interaction.reply({ content: 'You cannot untimeout yourself', ephemeral: true })
+ if (member.permissions.has(PermissionsBitField.Flags.Administrator)) return await interaction.reply({ content: 'You cannot untimeout this user', ephemeral: true })
+
+ await member.timeout(null).catch(console.error);
+ await interaction.reply({
+ content: `Untimed out ${user.tag}`,
+ ephemeral: true
+ })
+ }
+};
diff --git a/src/commands/tools/avatar.js b/src/commands/tools/avatar.js
new file mode 100644
index 0000000..06c7399
--- /dev/null
+++ b/src/commands/tools/avatar.js
@@ -0,0 +1,16 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('avatar')
+ .setDescription('Replies with your avatar!')
+ .addUserOption(option => option.setName('target').setDescription('The user\'s avatar to show')),
+ async execute(interaction) {
+ const user = interaction.options.getUser('target');
+ if (!user) {
+ await interaction.reply({ content: `${interaction.user.displayAvatarURL({ dynamic: true })}` });
+ } else {
+ await interaction.reply({ content: `${interaction.options.getUser('target').displayAvatarURL({ dynamic: true })}` });
+ }
+ },
+};
diff --git a/src/commands/tools/botstatus.js b/src/commands/tools/botstatus.js
new file mode 100644
index 0000000..279f588
--- /dev/null
+++ b/src/commands/tools/botstatus.js
@@ -0,0 +1,54 @@
+const { SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ActionRowBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('botstatus')
+ .setDescription('Gets information about the bot'),
+ async execute(interaction) {
+ const bot = interaction.client.user;
+ const botmem = await interaction.guild.members.fetch(bot.id);
+ let totalSeconds = (interaction.client.uptime / 1000);
+ let days = Math.floor(totalSeconds / 86400);
+ totalSeconds %= 86400;
+ let hours = Math.floor(totalSeconds / 3600);
+ totalSeconds %= 3600;
+ let minutes = Math.floor(totalSeconds / 60);
+ let seconds = Math.floor(totalSeconds % 60);
+ let uptime = `${days} days, ${hours} hours, ${minutes} minutes and ${seconds} seconds`;
+
+ const row = new ActionRowBuilder()
+ .addComponents(
+ new ButtonBuilder()
+ .setLabel('Invite')
+ .setStyle('Link')
+ .setURL('https://discord.com/api/oauth2/authorize?client_id=1087418361283092510&permissions=8&scope=bot%20applications.commands'),
+ new ButtonBuilder()
+ .setLabel('Code')
+ .setStyle('Link')
+ .setURL('https://github.com/isabelroses/blahaj')
+ )
+
+ const embed = new EmbedBuilder()
+ .setTitle('Bot Status')
+ .setColor([255, 255, 255])
+ .setThumbnail(bot.displayAvatarURL({ dynamic: true }))
+ .addFields({ name: 'Created At', value: ``, inline: false })
+ .addFields({ name: 'Joined At', value: ``, inline: false })
+ .addFields({ name: 'Ping', value: `${Math.round(interaction.client.ws.ping)}ms`, inline: false })
+ .addFields({ name: 'Servers', value: `${interaction.client.guilds.cache.size}`, inline: false })
+ .addFields({
+ name: 'Uptime', value: `\`\`\`${uptime}\`\`\``, inline: true
+ })
+ .addFields({ name: 'Roles', value: `${botmem.roles.cache.map(r => r).join(' ')}`, inline: false })
+ .setFooter({ text: "Bot ID: 1087418361283092510" })
+ .setTimestamp(Date.now())
+ .setAuthor({
+ name: bot.tag,
+ iconURL: bot.displayAvatarURL({ dynamic: true })
+ });
+ await interaction.reply({
+ embeds: [embed],
+ components: [row]
+ })
+ }
+};
\ No newline at end of file
diff --git a/src/commands/tools/embed.js b/src/commands/tools/embed.js
new file mode 100644
index 0000000..3f04064
--- /dev/null
+++ b/src/commands/tools/embed.js
@@ -0,0 +1,30 @@
+const { SlashCommandBuilder, EmbedBuilder, Embed } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('embed')
+ .setDescription('Sends an embed')
+ .addStringOption(option => option.setName('title').setDescription('The title of the embed').setRequired(true))
+ .addStringOption(option => option.setName('description').setDescription('The description of the embed').setRequired(true))
+ .addStringOption(option => option.setName('image').setDescription('Image').setRequired(false)),
+ async execute(interaction, client) {
+ const embed = new EmbedBuilder()
+ .setTitle(interaction.options.getString('title'))
+ .setDescription(interaction.options.getString('description'))
+ .setThumbnail(interaction.options.getString('image') || null)
+ .setColor([255, 255, 255])
+ .setThumbnail(interaction.guild.iconURL({ dynamic: true }))
+ .setFooter({
+ iconURL: client.user.displayAvatarURL({ dynamic: true }),
+ text: client.user.tag
+ })
+ .setTimestamp(Date.now())
+ .setAuthor({
+ name: interaction.user.tag,
+ iconURL: interaction.user.displayAvatarURL({ dynamic: true })
+ });
+ await interaction.reply({
+ embeds: [embed]
+ })
+ }
+};
\ No newline at end of file
diff --git a/src/commands/tools/serverinfo.js b/src/commands/tools/serverinfo.js
new file mode 100644
index 0000000..1e55c1f
--- /dev/null
+++ b/src/commands/tools/serverinfo.js
@@ -0,0 +1,47 @@
+const { SlashCommandBuilder, EmbedBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('serverinfo')
+ .setDescription('Replies with server info!'),
+ async execute(interaction) {
+
+ const { guild } = interaction;
+ const { name, ownerId, createdAt, memberCount } = guild;
+ const icon = guild.iconURL({ dynamic: true });
+ const roles = guild.roles.cache.size;
+ const emojies = guild.emojis.cache.size;
+ const id = guild.id;
+
+ let baseVerification = guild.verificationLevel;
+ let verificationLevel = '';
+
+ if (baseVerification == 0) verificationLevel = 'None';
+ if (baseVerification == 1) verificationLevel = 'Low';
+ if (baseVerification == 2) verificationLevel = 'Medium';
+ if (baseVerification == 3) verificationLevel = 'High';
+ if (baseVerification == 4) verificationLevel = 'Very High';
+
+ const embed = new EmbedBuilder()
+ .setTitle('Server Info')
+ .setThumbnail(icon)
+ .addFields({ name: 'Server Name', value: `${name}`, inline: false })
+ .addFields({ name: 'Server ID', value: `${id}`, inline: false })
+ .addFields({ name: 'Owner', value: `<@${ownerId}>`, inline: false })
+ .addFields({ name: 'Created At', value: ``, inline: false })
+ .addFields({ name: 'Member Count', value: `${memberCount}`, inline: false })
+ .addFields({ name: 'Verification Level', value: `${verificationLevel}`, inline: false })
+ .addFields({ name: 'Roles', value: `${roles}`, inline: true })
+ .addFields({ name: 'Emojis', value: `${emojies}`, inline: true })
+ .addFields({ name: 'Server Boosts', value: `${guild.premiumSubscriptionCount}`, inline: true })
+ .setColor([255, 255, 255])
+ .setFooter({
+ text: interaction.user.tag,
+ iconURL: interaction.user.displayAvatarURL({ dynamic: true })
+ })
+ .setTimestamp(Date.now())
+ .setAuthor({ name: name, iconURL: icon });
+
+ await interaction.reply({ embeds: [embed] });
+ },
+};
diff --git a/src/commands/tools/whois.js b/src/commands/tools/whois.js
new file mode 100644
index 0000000..b7bfe0a
--- /dev/null
+++ b/src/commands/tools/whois.js
@@ -0,0 +1,33 @@
+const { SlashCommandBuilder, EmbedBuilder } = require('@discordjs/builders');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('whois')
+ .setDescription('Gets information about a user')
+ .addUserOption(option => option.setName('user').setDescription('The user to get information about').setRequired(false)),
+ async execute(interaction) {
+ const user = interaction.options.getUser('user') || interaction.user;
+ const member = await interaction.guild.members.fetch(user.id);
+ const embed = new EmbedBuilder()
+ .setTitle(`${user.username}#${user.discriminator}`)
+ .setDescription(`ID: ${user.id}`)
+ .setColor([255, 255, 255])
+ .setThumbnail(user.displayAvatarURL({ dynamic: true }))
+ .addFields({ name: 'Created At', value: ``, inline: false })
+ .addFields({ name: 'Joined At', value: ``, inline: true })
+ .addFields({ name: 'Bot', value: `${user.bot}`, inline: false })
+ .addFields({ name: 'Roles', value: `${member.roles.cache.map(r => r).join(' ')}`, inline: false })
+ .setFooter({
+ iconURL: interaction.client.user.displayAvatarURL({ dynamic: true }),
+ text: interaction.client.user.tag
+ })
+ .setTimestamp(Date.now())
+ .setAuthor({
+ name: interaction.user.tag,
+ iconURL: interaction.user.displayAvatarURL({ dynamic: true })
+ });
+ await interaction.reply({
+ embeds: [embed]
+ })
+ }
+};
diff --git a/src/commands/userContext/getAvatar.js b/src/commands/userContext/getAvatar.js
new file mode 100644
index 0000000..96bf447
--- /dev/null
+++ b/src/commands/userContext/getAvatar.js
@@ -0,0 +1,13 @@
+const { ContextMenuCommandBuilder, ApplicationCommandType } = require('discord.js');
+
+module.exports = {
+ data: new ContextMenuCommandBuilder()
+ .setName('Avatar')
+ .setType(ApplicationCommandType.User),
+ async execute(interaction) {
+ const user = interaction.options.getUser('user');
+ await interaction.reply({
+ content: user.displayAvatarURL({ dynamic: true })
+ });
+ }
+};
diff --git a/src/commands/userContext/userinfo.js b/src/commands/userContext/userinfo.js
new file mode 100644
index 0000000..5ceb0b4
--- /dev/null
+++ b/src/commands/userContext/userinfo.js
@@ -0,0 +1,32 @@
+const { ContextMenuCommandBuilder, ApplicationCommandType, EmbedBuilder } = require('discord.js');
+
+module.exports = {
+ data: new ContextMenuCommandBuilder()
+ .setName('User Info')
+ .setType(ApplicationCommandType.User),
+ async execute(interaction) {
+ const user = interaction.options.getUser('user');
+ const member = await interaction.guild.members.fetch(user.id);
+ const embed = new EmbedBuilder()
+ .setTitle(`${user.username}#${user.discriminator}`)
+ .setDescription(`ID: ${user.id}`)
+ .setColor([255, 255, 255])
+ .setThumbnail(user.displayAvatarURL({ dynamic: true }))
+ .addFields({ name: 'Created At', value: ``, inline: false })
+ .addFields({ name: 'Joined At', value: ``, inline: true })
+ .addFields({ name: 'Bot', value: `${user.bot}`, inline: false })
+ .addFields({ name: 'Roles', value: `${member.roles.cache.map(r => r).join(' ')}`, inline: false })
+ .setFooter({
+ iconURL: user.displayAvatarURL({ dynamic: true }),
+ text: user.tag
+ })
+ .setTimestamp(Date.now())
+ .setAuthor({
+ name: user.tag,
+ iconURL: user.displayAvatarURL({ dynamic: true })
+ });
+ await interaction.reply({
+ embeds: [embed]
+ });
+ }
+};
\ No newline at end of file
diff --git a/src/components/buttons/github-btn.js b/src/components/buttons/github-btn.js
new file mode 100644
index 0000000..c77582d
--- /dev/null
+++ b/src/components/buttons/github-btn.js
@@ -0,0 +1,12 @@
+module.exports = {
+ data: {
+ name: 'github',
+ description: 'Github button',
+ type: 2
+ },
+ async execute(interaction) {
+ await interaction.reply({
+ content: 'https://github.com/isabelroses',
+ });
+ }
+}
diff --git a/src/events/client/interactionCreate.js b/src/events/client/interactionCreate.js
new file mode 100644
index 0000000..57a35f9
--- /dev/null
+++ b/src/events/client/interactionCreate.js
@@ -0,0 +1,39 @@
+module.exports = {
+ name: 'interactionCreate',
+ async execute(interaction, client) {
+ if (interaction.isChatInputCommand()) {
+ const { commands } = client;
+ const { commandName } = interaction;
+ const command = commands.get(commandName);
+ if (!command) return;
+ try {
+ await command.execute(interaction, client);
+ } catch (error) {
+ console.error(error);
+ await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
+ }
+ } else if (interaction.isButton()) {
+ const { buttons } = client;
+ const { customId } = interaction;
+ const button = buttons.get(customId);
+ if (!button) return;
+ try {
+ await button.execute(interaction, client);
+ } catch (error) {
+ console.error(error);
+ await interaction.reply({ content: 'There was an error while executing this button!', ephemeral: true });
+ }
+ } else if (interaction.isContextMenuCommand()) {
+ const { commands } = client;
+ const { commandName } = interaction;
+ const contextCommand = commands.get(commandName);
+ if (!contextCommand) return;
+ try {
+ await contextCommand.execute(interaction, client);
+ } catch (error) {
+ console.error(error);
+ await interaction.reply({ content: 'There was an error while executing this context menu command!', ephemeral: true });
+ }
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/events/client/ready.js b/src/events/client/ready.js
new file mode 100644
index 0000000..ad3a995
--- /dev/null
+++ b/src/events/client/ready.js
@@ -0,0 +1,9 @@
+module.exports = {
+ name: 'ready',
+ once: true,
+ execute(client) {
+ console.log(`Logged in as ${client.user.tag}!`);
+ client.handleCommands();
+ client.pickPresence();
+ }
+};
\ No newline at end of file
diff --git a/src/functions/handlers/commands.js b/src/functions/handlers/commands.js
new file mode 100644
index 0000000..a68570c
--- /dev/null
+++ b/src/functions/handlers/commands.js
@@ -0,0 +1,30 @@
+const { REST } = require('@discordjs/rest');
+const { Routes } = require('discord-api-types/v9');
+const fs = require('fs');
+
+module.exports = (client) => {
+ client.handleCommands = async () => {
+ const commandFolders = fs.readdirSync('./src/commands');
+ for (const folder of commandFolders) {
+ const commandFiles = fs.readdirSync(`./src/commands/${folder}`).filter(file => file.endsWith('.js'));
+ const { commands, commandArray } = client;
+ for (const file of commandFiles) {
+ const command = require(`../../commands/${folder}/${file}`);
+ commands.set(command.data.name, command);
+ commandArray.push(command.data.toJSON());
+ }
+ }
+ const guild_ids = client.guilds.cache.map(guild => guild.id);
+ const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
+ try {
+ console.log('Started refreshing application (/) commands.');
+ for (const guildId of guild_ids) {
+ await rest.put(Routes.applicationGuildCommands(process.env.CLIENT_ID, guildId),
+ { body: client.commandArray }
+ ).then(() => console.log('Successfully updated commands for guild ' + guildId)).catch(console.error);
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+}
diff --git a/src/functions/handlers/components.js b/src/functions/handlers/components.js
new file mode 100644
index 0000000..6135463
--- /dev/null
+++ b/src/functions/handlers/components.js
@@ -0,0 +1,21 @@
+const { readdirSync } = require('fs');
+
+module.exports = (client) => {
+ client.handleComponents = async () => {
+ const componentFolders = readdirSync('./src/components');
+ for (const folder of componentFolders) {
+ const componentFiles = readdirSync(`./src/components/${folder}`).filter(file => file.endsWith('.js'));
+ const { buttons } = client;
+ switch (folder) {
+ case 'buttons':
+ for (const file of componentFiles) {
+ const button = require(`../../components/${folder}/${file}`);
+ buttons.set(button.data.name, button);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/functions/handlers/events.js b/src/functions/handlers/events.js
new file mode 100644
index 0000000..b3223dd
--- /dev/null
+++ b/src/functions/handlers/events.js
@@ -0,0 +1,22 @@
+const fs = require('fs');
+
+module.exports = (client) => {
+ client.handleEvents = async () => {
+ const eventFolders = fs.readdirSync('./src/events');
+ for (const folder of eventFolders) {
+ const eventFiles = fs.readdirSync(`./src/events/${folder}`).filter((file) => file.endsWith('.js'));
+ switch (folder) {
+ case "client":
+ for (const file of eventFiles) {
+ const event = require(`../../events/${folder}/${file}`);
+ if (event.once) client.once(event.name, (...args) => event.execute(...args, client));
+ else client.on(event.name, (...args) => event.execute(...args, client));
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/functions/tools/pickPresence.js b/src/functions/tools/pickPresence.js
new file mode 100644
index 0000000..2afdfd6
--- /dev/null
+++ b/src/functions/tools/pickPresence.js
@@ -0,0 +1,21 @@
+const { ActivityType } = require('discord.js');
+
+module.exports = (client) => {
+ client.pickPresence = async () => {
+ const options = [
+ { text: 'with Slash Commands', type: ActivityType.Playing, url: null, status: 'online' },
+ { text: '/help', type: ActivityType.Watching, url: null, status: 'online' },
+ ];
+ const option = Math.floor(Math.random() * options.length);
+
+ client.user.setPresence({
+ activities: [{
+ name: options[option].text,
+ type: options[option].type,
+ url: options[option].url,
+ }],
+ status: options[option].status,
+ });
+ console.log(`Presence set to ${options[option].text}`);
+ }
+}