mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-03 09:20:08 +00:00
Setup turborepo (#2610)
* chore: BREAKING move to monorepo structure * chore: setup turborepo
This commit is contained in:
1
examples/nodejs/.env.example
Normal file
1
examples/nodejs/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
TOKEN=
|
||||
136
examples/nodejs/Managers/CommandManager.js
Normal file
136
examples/nodejs/Managers/CommandManager.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const resolveFolder = (folderName) => path.resolve(__dirname, ".", folderName);
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
class CommandManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
this.cache = new Map();
|
||||
this.aliases = new Map();
|
||||
}
|
||||
load(options = {}) {
|
||||
const commandFolderPath = options.path || "../Plugins";
|
||||
const commandFolder = resolveFolder(commandFolderPath);
|
||||
if (options.category === undefined) options.category = true;
|
||||
if (options.plugins === undefined) options.plugins = true;
|
||||
//PluginMode will iterate through all SubFolders
|
||||
fs.readdirSync(commandFolder).map(async (dir) => {
|
||||
if (dir.endsWith(".txt")) return;
|
||||
if (!options.category && dir.endsWith(".js")) {
|
||||
const commandPath = path.join(commandFolder, dir);
|
||||
this.loadCommand(commandPath);
|
||||
} else {
|
||||
fs.readdirSync(path.join(commandFolder, dir)).map((cmd) => {
|
||||
if (cmd.endsWith(".js") && !options.plugins) {
|
||||
const commandPath = path.join(commandFolder, dir, cmd);
|
||||
this.loadCommand(commandPath);
|
||||
} else if (commandFolderPath === "../Plugins") {
|
||||
if (cmd !== "commands") return;
|
||||
fs.readdirSync(path.join(commandFolder, dir, cmd)).map((cmdfile) => {
|
||||
if (!cmdfile.endsWith(".js")) return;
|
||||
const commandPath = path.join(commandFolder, dir, cmd, cmdfile);
|
||||
this.loadCommand(commandPath);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadCommand(commandPath) {
|
||||
const pull = require(path.join(commandPath));
|
||||
if (pull.name) {
|
||||
pull.path = commandPath;
|
||||
this.cache.set(pull.name, pull);
|
||||
}
|
||||
if (pull.aliases) {
|
||||
pull.aliases.map((p) => this.aliases.set(p, pull));
|
||||
}
|
||||
return pull;
|
||||
}
|
||||
|
||||
reloadCommand(commandName) {
|
||||
const command = this.cache.get(commandName);
|
||||
if (!command) return;
|
||||
const commandPath = path.join(command.path);
|
||||
delete require.cache[require.resolve(commandPath)];
|
||||
return this.loadCommand(commandPath);
|
||||
}
|
||||
|
||||
isCommand(message) {
|
||||
if (message.isFromBot) return false;
|
||||
const prefix = "!";
|
||||
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const prefixRegex = new RegExp(`^(<@!?${this.client.id}>|${escapeRegex(prefix)})\\s*`);
|
||||
if (!prefixRegex.test(message.content)) return false;
|
||||
|
||||
const [, matchedPrefix] = message.content.match(prefixRegex);
|
||||
const args = message.content.slice(matchedPrefix.length).trim().split(/ +/);
|
||||
|
||||
this.onMessage(message, prefix, args);
|
||||
return true;
|
||||
}
|
||||
|
||||
isInteraction(interaction) {
|
||||
if (interaction.type !== 2) return;
|
||||
this.onInteraction(interaction);
|
||||
}
|
||||
|
||||
async onMessage(message, guild, args) {
|
||||
const commandName = args.shift().toLowerCase();
|
||||
const command = this.cache.get(commandName); //|| this.cache.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
|
||||
if (!command && message.content.includes(this.client.id)) {
|
||||
//Handle, when Command has not been found
|
||||
const options = { content: "I did not found the Command!" };
|
||||
this.client.helpers.sendMessage(message.channelId, options);
|
||||
}
|
||||
if (!command) return;
|
||||
|
||||
const messagecommand = new command({
|
||||
manager: this,
|
||||
message: message,
|
||||
client: this.client,
|
||||
args: args,
|
||||
settings: {},
|
||||
commandName: command.name,
|
||||
});
|
||||
messagecommand.execute()?.catch?.((error) => {
|
||||
console.log(error);
|
||||
// Call Function on CommandResponse.js, handle the error
|
||||
return messagecommand.onError(error ?? "custom");
|
||||
});
|
||||
}
|
||||
|
||||
async onInteraction(interaction) {
|
||||
const command = this.cache.get(interaction.data.name);
|
||||
if (!command) return;
|
||||
|
||||
const args = [];
|
||||
//Map all Values and Args
|
||||
interaction.data.options?.map((o) => {
|
||||
if (o.name) args.push(o.name);
|
||||
if (o.options) {
|
||||
o.options.map((o2) => {
|
||||
if (o2.value) return args.push(o2.value);
|
||||
if (o2.name) args.push(o2.name);
|
||||
if (o2.options) o2.options.map((v) => args.push(v.value));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const messagecommand = new command({
|
||||
manager: this,
|
||||
interaction: interaction,
|
||||
client: this.client,
|
||||
args: args,
|
||||
settings: {},
|
||||
commandName: command.name,
|
||||
});
|
||||
messagecommand.execute()?.catch?.((error) => {
|
||||
console.log(error);
|
||||
// Call Function on CommandResponse.js, handle the error
|
||||
return messagecommand.onError(error ?? "custom");
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = CommandManager;
|
||||
32
examples/nodejs/Managers/EventManager.js
Normal file
32
examples/nodejs/Managers/EventManager.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const resolveFolder = (folderName) => path.resolve(__dirname, ".", folderName);
|
||||
|
||||
const EventEmitter = require("events");
|
||||
|
||||
class EventManager extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.cache = new Map();
|
||||
this.allEvents = {};
|
||||
}
|
||||
|
||||
load(options = {}) {
|
||||
const eventsFolder = resolveFolder("../events");
|
||||
let i = 0;
|
||||
fs.readdirSync(eventsFolder).map(async (file) => {
|
||||
if (!file.endsWith(".js")) return;
|
||||
i++;
|
||||
const fileName = path.join(eventsFolder, file);
|
||||
const event = require(fileName);
|
||||
const eventName = file.split(".")[0];
|
||||
|
||||
this.allEvents[`${eventName}`] = (...args) => {
|
||||
this.emit(eventName, ...args);
|
||||
return event(...args);
|
||||
};
|
||||
});
|
||||
return this.allEvents;
|
||||
}
|
||||
}
|
||||
module.exports = EventManager;
|
||||
50
examples/nodejs/Plugins/Developer/commands/eval.js
Normal file
50
examples/nodejs/Plugins/Developer/commands/eval.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const Discord = require("discordeno.js");
|
||||
|
||||
const BaseCommand = require("../../../Structures/BaseCommand.js");
|
||||
class evalcommand extends BaseCommand {
|
||||
static name = "eval";
|
||||
static description = "danger !!!";
|
||||
static category = "Developer";
|
||||
static slash = { name: "eval", category: "dev" };
|
||||
constructor(data) {
|
||||
super(data);
|
||||
}
|
||||
async execute() {
|
||||
if (!this.client.config.owners.includes(String(this.user.id))) return;
|
||||
if (!(this.args.length > 0)) return this.reply({ content: "**You must provide something to eval!**" });
|
||||
|
||||
let inputOfEval = this.args.join(" ");
|
||||
let outputOfEval;
|
||||
let typeOfEval;
|
||||
|
||||
try {
|
||||
if (this.args.includes("await")) {
|
||||
outputOfEval = await eval("(async () => {" + inputOfEval + "})()");
|
||||
} else {
|
||||
outputOfEval = await eval(inputOfEval);
|
||||
}
|
||||
} catch (e) {
|
||||
outputOfEval = e.message;
|
||||
typeOfEval = e.name;
|
||||
}
|
||||
|
||||
var seen = [];
|
||||
outputOfEval = typeof outputOfEval === "object"
|
||||
? JSON.stringify(outputOfEval, (_, value) => {
|
||||
if (value == `Bot ${this.client.config.token}`) return `BOT_TOKEN`;
|
||||
if (typeof value === "bigint") value = value.toString();
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (seen.indexOf(value) !== -1) return;
|
||||
else seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}, 1)
|
||||
: outputOfEval;
|
||||
|
||||
const embed = new Discord.Embed()
|
||||
.addField({ name: "Input", value: "```js\n" + inputOfEval + "```" })
|
||||
.addField({ name: "Output", value: "```json\n" + `${outputOfEval}`.slice(0, 1000) + "```" });
|
||||
this.reply({ embeds: [embed] });
|
||||
}
|
||||
}
|
||||
module.exports = evalcommand;
|
||||
18
examples/nodejs/Plugins/Developer/commands/reload.js
Normal file
18
examples/nodejs/Plugins/Developer/commands/reload.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const BaseCommand = require("../../../Structures/BaseCommand.js");
|
||||
class reloadcommand extends BaseCommand {
|
||||
static name = "reload";
|
||||
static description = "Reloads a Command";
|
||||
static category = "Developer";
|
||||
static slash = { name: "reload", category: "dev" };
|
||||
constructor(data) {
|
||||
super(data);
|
||||
}
|
||||
async execute() {
|
||||
if (!this.client.config.owners.includes(String(this.user.id))) return;
|
||||
if (!this.args[0]) return this.reply({ content: "**You must provide a command to reload!**" });
|
||||
const op = this.client.commands.reloadCommand(this.args[0]);
|
||||
if (!op) return this.reply({ content: "**That command doesn't exist!**" });
|
||||
return this.reply({ content: "**Reloaded Command: `" + this.args[0] + "`**" });
|
||||
}
|
||||
}
|
||||
module.exports = reloadcommand;
|
||||
0
examples/nodejs/Plugins/Developer/index.js
Normal file
0
examples/nodejs/Plugins/Developer/index.js
Normal file
24
examples/nodejs/Plugins/General/commands/ping.js
Normal file
24
examples/nodejs/Plugins/General/commands/ping.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const BaseCommand = require("../../../Structures/BaseCommand.js");
|
||||
const Discord = require("discordeno.js");
|
||||
class pingcommand extends BaseCommand {
|
||||
static name = "ping";
|
||||
static description = "See if the bot latency is okay";
|
||||
static usage = "";
|
||||
static category = "General";
|
||||
static slash = { name: "ping", category: "info" };
|
||||
constructor(data) {
|
||||
super(data);
|
||||
}
|
||||
async execute() {
|
||||
const msg = await this.channel.send({ content: `Pinging...` });
|
||||
//Assign properties to the response
|
||||
const ping = msg.timestamp - (this.message ? this.message.timestamp : this.interaction.timestamp);
|
||||
|
||||
const embed = new Discord.Embed()
|
||||
.setTitle(`The Bots ping is ${ping} ms`)
|
||||
.toJSON();
|
||||
//Edit Message with the Embed
|
||||
return this.reply({ embeds: [embed] });
|
||||
}
|
||||
}
|
||||
module.exports = pingcommand;
|
||||
0
examples/nodejs/Plugins/General/index.js
Normal file
0
examples/nodejs/Plugins/General/index.js
Normal file
40
examples/nodejs/Plugins/Moderation/commands/ban.js
Normal file
40
examples/nodejs/Plugins/Moderation/commands/ban.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const BaseCommand = require("../../../Structures/BaseCommand.js");
|
||||
const Discord = require("discordeno.js");
|
||||
|
||||
class bancommand extends BaseCommand {
|
||||
static name = "ban";
|
||||
static description = "Ban a user from the server";
|
||||
static usage = "";
|
||||
static category = "Moderation";
|
||||
static slash = { name: "ban", category: "mod" };
|
||||
constructor(data) {
|
||||
super(data);
|
||||
}
|
||||
async execute() {
|
||||
//Show Case Modal
|
||||
|
||||
// Because no permission system has not been added
|
||||
if (!this.client.config.owners.includes(String(this.user.id))) return;
|
||||
|
||||
const textinput = new Discord.Component()
|
||||
.setType("TEXT_INPUT")
|
||||
.setStyle("SHORT")
|
||||
.setCustomId("t1")
|
||||
.setLabel("User ID")
|
||||
.setPlaceholder("User ID")
|
||||
.setRequired(true)
|
||||
.setMaxLength(20)
|
||||
.setMinLength(1)
|
||||
.setValue(this.args[0])
|
||||
.toJSON();
|
||||
const textinput2 = new Discord.Component().setType("TEXT_INPUT").setStyle("PARAGRAPH").setCustomId("t2")
|
||||
.setLabel("Reason").setPlaceholder("Reason for Ban").setRequired(false)
|
||||
.setMaxLength(300).toJSON();
|
||||
|
||||
const actionrow = new Discord.Component().setType(1).setComponents(textinput).toJSON();
|
||||
const actionrow2 = new Discord.Component().setType(1).setComponents(textinput2).toJSON();
|
||||
|
||||
this.interaction.popupModal({ customId: "ban_modal", title: "Ban User", components: [actionrow, actionrow2] });
|
||||
}
|
||||
}
|
||||
module.exports = bancommand;
|
||||
81
examples/nodejs/Plugins/Moderation/commands/warn.js
Normal file
81
examples/nodejs/Plugins/Moderation/commands/warn.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const BaseCommand = require("../../../Structures/BaseCommand.js");
|
||||
|
||||
const { Interaction, Collector, ComponentOptions, Embed, Component } = require("discordeno.js");
|
||||
|
||||
class warncommand extends BaseCommand {
|
||||
static name = "warn";
|
||||
static description = "Warn a user from the server";
|
||||
static usage = "";
|
||||
static category = "Moderation";
|
||||
static slash = { name: "warn", category: "mod" };
|
||||
constructor(data) {
|
||||
super(data);
|
||||
}
|
||||
async execute() {
|
||||
//Show Case Modal
|
||||
if (!this.interaction) return this.reply("You currently can just use this command as slash command.");
|
||||
|
||||
if (!this.interaction.member.permissions.has("KICK_MEMBERS")) {
|
||||
return this.reply("You need the permission `KICK_MEMBERS` to use this command.");
|
||||
}
|
||||
|
||||
const textinput = new Component()
|
||||
.setType("TEXT_INPUT")
|
||||
.setStyle("SHORT")
|
||||
.setCustomId("t1")
|
||||
.setLabel("User ID")
|
||||
.setPlaceholder("User ID")
|
||||
.setRequired(true)
|
||||
.setMaxLength(20)
|
||||
.setMinLength(1)
|
||||
.setValue(this.args[0])
|
||||
.toJSON();
|
||||
const textinput2 = new Component().setType("TEXT_INPUT").setStyle("PARAGRAPH").setCustomId("t2")
|
||||
.setLabel("Reason").setPlaceholder("Reason for Warning").setRequired(false)
|
||||
.setMaxLength(300).toJSON();
|
||||
|
||||
const actionrow = new Component().setType(1).setComponents(textinput).toJSON();
|
||||
const actionrow2 = new Component().setType(1).setComponents(textinput2).toJSON();
|
||||
|
||||
this.interaction.popupModal({ customId: "warn_modal", title: "Warn User", components: [actionrow, actionrow2] });
|
||||
|
||||
const filter = (m) => m.data?.customId === "warn_modal";
|
||||
const collector = new Collector("interactionCreate", { client: this.client, timeout: 60000, filter });
|
||||
collector.on("collect", (m) => {
|
||||
const options = new ComponentOptions(m.data.components);
|
||||
const i = new Interaction(this.client, m);
|
||||
collector.stop();
|
||||
|
||||
const memberId = options.get("t1").value;
|
||||
const reason = options.get("t2").value;
|
||||
|
||||
const embed = new Embed()
|
||||
.setTitle("Warned User:")
|
||||
.setDescription(`User ID: <@${memberId}> \n Reason: ${reason}`)
|
||||
.setColor(0x00ff00)
|
||||
.toJSON();
|
||||
|
||||
const warnMessage = new Embed()
|
||||
.setTitle("Warning:")
|
||||
.setDescription(`You have been warned in **${this.guild.name}** for ${"`" + reason + "`"}`)
|
||||
.toJSON();
|
||||
|
||||
this.guild.members.fetch(memberId).then((m) => {
|
||||
m.send({ embeds: [warnMessage] }).then(() => {
|
||||
i.reply({ embeds: [embed] });
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
i.reply({ content: `Could not warn user ${"<@" + m.id + ">"} | They likely do not have their DMs open.` });
|
||||
});
|
||||
}).catch((e) => {
|
||||
const embed = new Embed()
|
||||
.setTitle("Member not found")
|
||||
.setDescription(`The member with the ID of ${"`" + memberId + "`"} has not been found in this Server.`)
|
||||
.setColor(0xff0000)
|
||||
.toJSON();
|
||||
i.reply({ embeds: [embed] });
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = warncommand;
|
||||
0
examples/nodejs/Plugins/Moderation/index.js
Normal file
0
examples/nodejs/Plugins/Moderation/index.js
Normal file
13
examples/nodejs/README.md
Normal file
13
examples/nodejs/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Nodejs Bot Template
|
||||
|
||||
This template is a minimal template that uses [discordeno.js](https://www.npmjs.com/package/discordeno.js), a wrapper
|
||||
around discordeno, which brings up a djs-like interface but also includes the core Features (Cross Ratelimit, Zero
|
||||
Downtime Restart, Resharding...)
|
||||
|
||||
## Setup
|
||||
|
||||
Rename `.env.example` to `.env` and fill your bot token there.
|
||||
|
||||
## Run Bot
|
||||
|
||||
- node index.js
|
||||
15
examples/nodejs/Structures/BaseCommand.js
Normal file
15
examples/nodejs/Structures/BaseCommand.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const UtilCommand = require("./CommandResponse.js");
|
||||
class BaseCommand extends UtilCommand {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
this.message = data.message;
|
||||
this.interaction = data.interaction;
|
||||
this.user = this.message ? this.message.author : this.interaction.user;
|
||||
this.guild = this.message ? this.message.guild : this.interaction.guild;
|
||||
this.member = this.message ? this.message.member : this.interaction.member;
|
||||
this.channel = this.message ? this.message.channel : this.interaction.channel;
|
||||
this.client = data.client;
|
||||
this.settings = data.settings ?? {};
|
||||
}
|
||||
}
|
||||
module.exports = BaseCommand;
|
||||
50
examples/nodejs/Structures/CommandResponse.js
Normal file
50
examples/nodejs/Structures/CommandResponse.js
Normal file
@@ -0,0 +1,50 @@
|
||||
class Responses {
|
||||
constructor(data) {
|
||||
this.manager = data.manager;
|
||||
this.args = this._validateArguments(data.args);
|
||||
this.replied = false;
|
||||
}
|
||||
|
||||
async reply(content) {
|
||||
// When just a string is passed, we assume it's the content -> transform to correct formatted payload
|
||||
if (typeof content === "string") content = { content };
|
||||
if (this.interaction) {
|
||||
if (this.replied) return this.followUp(content);
|
||||
const reply = await this.interaction.reply(content);
|
||||
this.replied = true;
|
||||
return {};
|
||||
}
|
||||
if (this.message) {
|
||||
if (this.replied) return this.followUp(content);
|
||||
|
||||
const msg = await this.message.channel.send(content);
|
||||
|
||||
//Assign properties to the response
|
||||
const response = this.client.messages.forge(msg);
|
||||
this.replied = true;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
async followUp(content) {
|
||||
if (this.interaction) {
|
||||
const reply = await this.interaction.followUp(content);
|
||||
return {};
|
||||
}
|
||||
if (this.message) {
|
||||
const msg = await this.message.channel.send(content);
|
||||
const response = this.client.messages.forge(msg);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
return this.reply({ content: `A unknown Error happend: \n> ${error}` });
|
||||
}
|
||||
|
||||
_validateArguments(args) {
|
||||
this.args = args;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
module.exports = Responses;
|
||||
4
examples/nodejs/events/interactionCreate.js
Normal file
4
examples/nodejs/events/interactionCreate.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = async (client, interaction) => {
|
||||
interaction = client.interactions.forge(interaction);
|
||||
client.commands.isInteraction(interaction);
|
||||
};
|
||||
4
examples/nodejs/events/messageCreate.js
Normal file
4
examples/nodejs/events/messageCreate.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = async (client, message) => {
|
||||
message = client.messages.forge(message);
|
||||
client.commands.isCommand(message);
|
||||
};
|
||||
7
examples/nodejs/events/ready.js
Normal file
7
examples/nodejs/events/ready.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = async (client, payload) => {
|
||||
client.user = client.users.forge(payload.user);
|
||||
if (payload.shardId === client.gateway.lastShardId) {
|
||||
//All Shards are ready
|
||||
console.log("Successfully connected to the gateway as " + client.user.tag);
|
||||
}
|
||||
};
|
||||
32
examples/nodejs/index.js
Normal file
32
examples/nodejs/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
require("dotenv").config();
|
||||
|
||||
const Discord = require("discordeno.js");
|
||||
|
||||
// Ideally you should switch this to .env but for a template a config json is enough
|
||||
const config = require("./config.json");
|
||||
|
||||
const EventManager = require("./Managers/EventManager.js");
|
||||
// looping through all events and registering them
|
||||
const events = new EventManager({});
|
||||
|
||||
const baseBot = Discord.createBot({
|
||||
events: events.load({}),
|
||||
intents: Discord.Intents.Guilds | Discord.Intents.GuildMessages | Discord.Intents.MessageContent,
|
||||
token: process.env.TOKEN,
|
||||
});
|
||||
const client = Discord.enableCachePlugin(baseBot, {});
|
||||
|
||||
client.config = config;
|
||||
|
||||
// looping through all commands and registering them in .cache of the class
|
||||
const CommandManager = require("./Managers/CommandManager.js");
|
||||
client.commands = new CommandManager(client);
|
||||
client.commands.load({});
|
||||
|
||||
// Starts your Bot
|
||||
Discord.startBot(client);
|
||||
|
||||
/*
|
||||
* You should handle all errors and fix the issues in your codes...
|
||||
* process.on('unhandledRejection', (reason, p) => {console.log(reason, p)})
|
||||
*/
|
||||
16
examples/nodejs/package.json
Normal file
16
examples/nodejs/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "nodejs-template",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discordeno.js": "github:meister03/discordeno.js",
|
||||
"dotenv": "^16.0.3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user