diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6483131
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+
+
+
+
+---
+
+## What's this?
+
+OPCommands is an advanced Discord.js Command Handler that supports slash commands and more!
+
+## Requirements
+
+* Discord.js v13+
+* Node.js v16.6.0+
+
+## Installation
+
+```bash
+npm install --save opcommands
+```
+
+## Documentation
+
+Here is the official Documentation of OPCommands: https://opcommands.lukedev.tk
+
+## Contributing
+
+Feel free to report bugs, suggest new things or make pull requests to the GitHub repository of OPCommands. It would be really appreciated!
\ No newline at end of file
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644
index 0000000..618f2f0
Binary files /dev/null and b/assets/logo.png differ
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..94e7a8b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "opcommands",
+ "version": "1.0.0",
+ "description": "An advanced Discord.js Command Handler that supports slash commands and more!",
+ "main": "src/OPCommands.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [
+ "discord.js",
+ "discord",
+ "handler",
+ "command",
+ "slash"
+ ],
+ "author": "LukeIsHereToDevelop (https://lukedev.tk)",
+ "homepage": "https://opcommands.lukedev.tk",
+ "bugs": {
+ "url": "https://github.com/LukeIsHereToDevelop/opcommands/issues"
+ },
+ "repository": "github:LukeIsHereToDevelop/opcommands",
+ "license": "GPL-3.0-or-later",
+ "engines": {
+ "node": "^16.6.0"
+ },
+ "dependencies": {
+ "@discordjs/rest": "^0.1.0-canary.0",
+ "discord-api-types": "^0.22.0",
+ "discord.js": "^13.0.1",
+ "humanize-ms": "^1.2.1",
+ "ms": "^2.1.3"
+ }
+}
diff --git a/src/OPCommands.js b/src/OPCommands.js
new file mode 100644
index 0000000..40346a2
--- /dev/null
+++ b/src/OPCommands.js
@@ -0,0 +1,65 @@
+const Discord = require("discord.js");
+const EventHandler = require("./handlers/Event.js");
+const CommandHandler = require("./handlers/Command.js");
+
+/**
+ * The main class of OPCommands.
+ * @class
+ * @requires [Node.js v16.6.0](https://nodejs.org/en/download/current/) & Discord.js v13
+ * @param {Discord.Client} client
+ * @param {Object} options
+ * @example
+ * const OPCommands = require("opcommands");
+ * const Discord = require("discord.js");
+ * const client = new Discord.Client();
+ * new OPCommands(client, {
+ * commandsDir: "commands",
+ * eventsDir: "events"
+ * });
+ */
+class OPCommands {
+ constructor(client, options) {
+ if (!client) throw new Error("[OPCommands] Missing Discord client.");
+ if (!options) throw new Error("[OPCommands] Missing options.")
+ if (!options.commandsDir) throw new Error("[OPCommands] Missing commands' directory parameter on options.");
+ if (!options.eventsDir) throw new Error("[OPCommands] Missing events' directory parameter on options.");
+ this.client = client;
+ this.options = options;
+
+ new CommandHandler(this, options.commandsDir);
+ new EventHandler(this, options.eventsDir);
+ }
+
+ /**
+ * Sets the owner(s) of the bot.
+ * @param {*} owner Owner ID(s)
+ */
+ async setBotOwner(owner) {
+ if (!owner) throw new Error("[OPCommands] Missing Owner ID(s).");
+ if (typeof owner === "string") return this.client.owners = [owner];
+ if (typeof owner === "array") return this.client.owners = owner;
+ throw new Error("[OPCommands] Owner parameter must be a String or Array.");
+ }
+
+ /**
+ * Adds owner(s) of the bot.
+ * @param {String} owner Owner ID
+ */
+ async addBotOwner(owner) {
+ if (!owner) throw new Error("[OPCommands] Missing Owner ID.");
+ if (typeof owner != "string") throw new Error("[OPCommands] Owner parameter must be a String.");
+ return this.client.owners.push(owner);
+ }
+
+ /**
+ * Sets the limit messages.
+ * @param {Object} msgs New messages
+ */
+ async setMessages(msgs) {
+ if (!msgs) throw new Error("[OPCommands] Missing the new messages.");
+ if (typeof msgs != "object") throw new Error("[OPCommands] Msgs parameter must be an Object.");
+ return this.client.msgs = msgs;
+ }
+}
+
+module.exports = OPCommands;
\ No newline at end of file
diff --git a/src/handlers/Command.js b/src/handlers/Command.js
new file mode 100644
index 0000000..5dc7765
--- /dev/null
+++ b/src/handlers/Command.js
@@ -0,0 +1,102 @@
+const Discord = require("discord.js");
+const { REST } = require('@discordjs/rest');
+const { Routes } = require('discord-api-types/v9');
+const hms = require("humanize-ms");
+const ms = require("ms");
+const fs = require("fs");
+const path = require("path");
+const { time } = require("console");
+
+/**
+ * Command Handler
+ * @class
+ * @param {*} _this
+ * @param {String} commandsDir
+ */
+class CommandHandler {
+ constructor (_this, commandsDir) {
+ if (!this) throw new Error("[OPCommands] Internal error: missing _this parameter on Command Handler.");
+ if (!commandsDir) throw new Error("[OPCommands] Internal error: missing eventsDir parameter on Command Handler.");
+ if (!fs.existsSync(commandsDir)) throw new Error("[OPCommands] Unexisting command directory.");
+
+ _this.client.commands = new Discord.Collection();
+ const files = fs.readdirSync(commandsDir).filter(file => file.endsWith(".js"));
+ const commands = [];
+
+ if (_this.options.logs) console.log("[OPCommands] Loaded " + files.length + " commands.");
+ for (const file of files) {
+ const commandFile = require(path.join(require.main.path, commandsDir, file));
+ _this.client.commands.set(commandFile.name, commandFile);
+ const commandObj = {
+ name: commandFile.name,
+ description: commandFile.description
+ };
+ if (commandFile.options) Object.assign(commandObj, {options: commandFile.options});
+ commands.push(JSON.stringify(commandObj));
+ };
+
+ _this.client.on("ready", async () => {
+ const rest = new REST({ version: "9" }).setToken(_this.client.token);
+
+ (async () => {
+ try {
+ await rest.put(
+ Routes.applicationGuildCommands(_this.client.application?.id, "678352388251451433"),
+ { body: _this.client.commands },
+ );
+ } catch (error) {
+ console.error(error);
+ }
+ })();
+ });
+
+ const cooldowns = new Discord.Collection();
+ _this.client.on("interactionCreate", (interaction) => {
+ if (!interaction.isCommand()) return;
+ if (!_this.client.commands.has(interaction.commandName)) return;
+ const commandFile = _this.client.commands.get(interaction.commandName);
+ if (commandFile.limits.owner && !_this.client.owners.includes(interaction.user.id)) {
+ const ownerOnly = _this.client.msgs.ownerOnly;
+ if (ownerOnly) {
+ return ownerOnly(interaction);
+ } else {
+ return interaction.reply({ content: "You must be a Bot Owner to execute this command!", ephemeral: true });
+ };
+ };
+ if (commandFile.limits.permissions && !interaction.member?.permissions.has(commandFile.limits.permissions)) {
+ const permissions = _this.client.msgs.permissions;
+ if (permissions) {
+ return permissions(interaction, commandFile.limits.permissions);
+ } else {
+ return interaction.reply({ content: "You must have the following permissions: " + commandFile.limits.permissions.join(", "), ephemeral: true });
+ };
+ };
+ if (!cooldowns.has(`${commandFile.name}_${interaction.user.id}`)) {
+ if (!commandFile.limits.cooldown) return;
+ cooldowns.set(`${commandFile.name}_${interaction.user.id}`, Date.now());
+ setTimeout(() => {cooldowns.delete(`${commandFile.name}_${interaction.user.id}`)}, hms(commandFile.limits.cooldown))
+ } else {
+ const expirationTime = cooldowns.get(`${commandFile.name}_${interaction.user.id}`) + hms(commandFile.limits.cooldown);
+ if (Date.now() < expirationTime) {
+ const timeLeft = ms(expirationTime - Date.now());
+ const cooldown = _this.client.msgs.cooldown;
+ if (cooldown) {
+ return cooldown(interaction, timeLeft);
+ } else {
+ return interaction.reply({ content: `You are in a cooldown! Please wait **${timeLeft}**.`, ephemeral: true });
+ };
+ };
+ };
+
+ try {
+ if (_this.options.logs) console.log("[OPCommands] Command executed: " + interaction.commandName);
+ _this.client.commands.get(interaction.commandName).run(_this.client, interaction);
+ } catch (e) {
+ if (_this.options.logs) console.log("[OPCommands] Command error: " + interaction.commandName);
+ console.error(e);
+ }
+ });
+ }
+}
+
+module.exports = CommandHandler;
\ No newline at end of file
diff --git a/src/handlers/Event.js b/src/handlers/Event.js
new file mode 100644
index 0000000..c480b24
--- /dev/null
+++ b/src/handlers/Event.js
@@ -0,0 +1,31 @@
+const Discord = require("discord.js");
+const fs = require("fs");
+const path = require("path");
+
+/**
+ * Event Handler.
+ * @class
+ * @param {*} _this
+ * @param {String} eventsDir
+ */
+class EventHandler {
+ constructor(_this, eventsDir) {
+ if (!_this) throw new Error("[OPCommands] Internal error: missing _this parameter on Event Handler.");
+ if (!eventsDir) throw new Error("[OPCommands] Internal error: missing eventsDir parameter on Event Handler.");
+ if (!fs.existsSync(eventsDir)) throw new Error("[OPCommands] Unexisting event directory.");
+
+ const files = fs.readdirSync(eventsDir).filter(file => file.endsWith(".js"));
+
+ if (_this.options.logs) console.log("[OPCommands] Loaded " + files.length + " events.");
+ for (const file of files) {
+ const eventFile = require(path.join(require.main.path, eventsDir, file));
+ if (eventFile.settings.once) {
+ _this.client.once(eventFile.name, (...args) => eventFile.run(_this.client, ...args));
+ } else {
+ _this.client.on(eventFile.name, (...args) => eventFile.run(_this.client, ...args));
+ };
+ }
+ }
+}
+
+module.exports = EventHandler;
\ No newline at end of file
diff --git a/test/commands/say.js b/test/commands/say.js
new file mode 100644
index 0000000..ad0c4c3
--- /dev/null
+++ b/test/commands/say.js
@@ -0,0 +1,20 @@
+module.exports = {
+ name: "say",
+ description: "Says something.",
+ options: [
+ {
+ type: 3,
+ name: "input",
+ description: "The input to return.",
+ required: true
+ }
+ ],
+ limits: {
+ owner: false,
+ permissions: ["MANAGE_MESSAGES"],
+ cooldown: "3s"
+ },
+ run: (client, interaction) => {
+ interaction.reply({ content: interaction.options.getString("input") });
+ }
+}
\ No newline at end of file
diff --git a/test/commands/test.js b/test/commands/test.js
new file mode 100644
index 0000000..5a78538
--- /dev/null
+++ b/test/commands/test.js
@@ -0,0 +1,12 @@
+module.exports = {
+ name: "test",
+ description: "Test command",
+ limits: {
+ owner: true,
+ permissions: ["ADMINISTRATOR"],
+ cooldown: "20s"
+ },
+ run: (client, interaction) => {
+ interaction.reply({ content: "test" });
+ }
+}
\ No newline at end of file
diff --git a/test/events/ready.js b/test/events/ready.js
new file mode 100644
index 0000000..8b8d7b6
--- /dev/null
+++ b/test/events/ready.js
@@ -0,0 +1,10 @@
+module.exports = {
+ name: "ready",
+ settings: {
+ once: false
+ },
+ run: (client) => {
+ console.log("I'm online!");
+ client.user.setActivity("OPCommands");
+ }
+}
\ No newline at end of file
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..7c2db3e
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,19 @@
+const OPCommands = require("../src/OPCommands.js");
+const Discord = require("discord.js");
+const client = new Discord.Client({
+ intents: 32767
+});
+
+const handler = new OPCommands(client, {
+ commandsDir: "commands",
+ eventsDir: "events",
+ logs: true
+});
+handler.setBotOwner("OWNER_ID");
+handler.setMessages({
+ ownerOnly: (interaction) => interaction.reply("Missing **Bot Owner** permission."),
+ permissions: (interaction, perms) => interaction.reply(`You are missing the following permissions: **${perms.join(", ")}**`),
+ cooldown: (interaction, cooldown) => interaction.reply(`You must wait **${cooldown}** before executing another command.`)
+});
+
+client.login("BOT_TOKEN");
\ No newline at end of file