Skip to content

Commit

Permalink
✏️ v1.0 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
LukeIsHereToDevelop authored Aug 16, 2021
1 parent 216c381 commit f1b494d
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 0 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<h1 align="center">
<a href="https://github.com/LukeIsHereToDevelop/opcommands"><img src="assets/logo.png" alt="OPCommands"></a>
</h1>

---

## 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!
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]> (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"
}
}
65 changes: 65 additions & 0 deletions src/OPCommands.js
Original file line number Diff line number Diff line change
@@ -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;
102 changes: 102 additions & 0 deletions src/handlers/Command.js
Original file line number Diff line number Diff line change
@@ -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;
31 changes: 31 additions & 0 deletions src/handlers/Event.js
Original file line number Diff line number Diff line change
@@ -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;
20 changes: 20 additions & 0 deletions test/commands/say.js
Original file line number Diff line number Diff line change
@@ -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") });
}
}
12 changes: 12 additions & 0 deletions test/commands/test.js
Original file line number Diff line number Diff line change
@@ -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" });
}
}
10 changes: 10 additions & 0 deletions test/events/ready.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
name: "ready",
settings: {
once: false
},
run: (client) => {
console.log("I'm online!");
client.user.setActivity("OPCommands");
}
}
19 changes: 19 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -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");

0 comments on commit f1b494d

Please sign in to comment.