@backs/bot-base
Version:
The 6th version of back's bot base.
1,427 lines (1,409 loc) • 54.7 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
Bot: () => Bot2,
ButtonBuilder: () => ButtonBuilder,
ButtonCommand: () => ButtonCommand,
ChannelSelectMenuBuilder: () => ChannelSelectMenuBuilder,
ContextMenuCommand: () => ContextMenuCommand,
Event: () => Event,
MentionableSelectMenuBuilder: () => MentionableSelectMenuBuilder,
MessageCommand: () => MessageCommand,
ModalBuilder: () => ModalBuilder,
ModalCommand: () => ModalCommand,
MultiPageEmbed: () => MultiPageEmbed,
PermissionsMode: () => PermissionsMode,
Plugin: () => Plugin,
RoleSelectMenuBuilder: () => RoleSelectMenuBuilder,
RunIn: () => RunIn,
SelectMenuCommand: () => SelectMenuCommand,
SlashCommand: () => SlashCommand,
StringSelectMenuBuilder: () => StringSelectMenuBuilder,
UserSelectMenuBuilder: () => UserSelectMenuBuilder,
default: () => createClient
});
module.exports = __toCommonJS(index_exports);
var import_discord8 = require("discord.js");
var import_utils3 = require("@backs/utils");
// node_modules/colorette/index.js
var tty = __toESM(require("tty"), 1);
var {
env = {},
argv = [],
platform = ""
} = typeof process === "undefined" ? {} : process;
var isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
var isForced = "FORCE_COLOR" in env || argv.includes("--color");
var isWindows = platform === "win32";
var isDumbTerminal = env.TERM === "dumb";
var isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal;
var isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
var isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
var replaceClose = (index, string, close, replace, head = string.substring(0, index) + replace, tail = string.substring(index + close.length), next = tail.indexOf(close)) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
var clearBleed = (index, string, open, close, replace) => index < 0 ? open + string + close : open + replaceClose(index, string, close, replace) + close;
var filterEmpty = (open, close, replace = open, at = open.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(
("" + string).indexOf(close, at),
string,
open,
close,
replace
) : "";
var init = (open, close, replace) => filterEmpty(`\x1B[${open}m`, `\x1B[${close}m`, replace);
var colors = {
reset: init(0, 0),
bold: init(1, 22, "\x1B[22m\x1B[1m"),
dim: init(2, 22, "\x1B[22m\x1B[2m"),
italic: init(3, 23),
underline: init(4, 24),
inverse: init(7, 27),
hidden: init(8, 28),
strikethrough: init(9, 29),
black: init(30, 39),
red: init(31, 39),
green: init(32, 39),
yellow: init(33, 39),
blue: init(34, 39),
magenta: init(35, 39),
cyan: init(36, 39),
white: init(37, 39),
gray: init(90, 39),
bgBlack: init(40, 49),
bgRed: init(41, 49),
bgGreen: init(42, 49),
bgYellow: init(43, 49),
bgBlue: init(44, 49),
bgMagenta: init(45, 49),
bgCyan: init(46, 49),
bgWhite: init(47, 49),
blackBright: init(90, 39),
redBright: init(91, 39),
greenBright: init(92, 39),
yellowBright: init(93, 39),
blueBright: init(94, 39),
magentaBright: init(95, 39),
cyanBright: init(96, 39),
whiteBright: init(97, 39),
bgBlackBright: init(100, 49),
bgRedBright: init(101, 49),
bgGreenBright: init(102, 49),
bgYellowBright: init(103, 49),
bgBlueBright: init(104, 49),
bgMagentaBright: init(105, 49),
bgCyanBright: init(106, 49),
bgWhiteBright: init(107, 49)
};
var createColors = ({ useColor = isColorSupported } = {}) => useColor ? colors : Object.keys(colors).reduce(
(colors2, key) => ({ ...colors2, [key]: String }),
{}
);
var {
reset,
bold,
dim,
italic,
underline,
inverse,
hidden,
strikethrough,
black,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
gray,
bgBlack,
bgRed,
bgGreen,
bgYellow,
bgBlue,
bgMagenta,
bgCyan,
bgWhite,
blackBright,
redBright,
greenBright,
yellowBright,
blueBright,
magentaBright,
cyanBright,
whiteBright,
bgBlackBright,
bgRedBright,
bgGreenBright,
bgYellowBright,
bgBlueBright,
bgMagentaBright,
bgCyanBright,
bgWhiteBright
} = createColors();
// src/index.ts
var import_glob = require("glob");
var import_path = __toESM(require("path"));
var import_is_glob = __toESM(require("is-glob"));
var import_logger = __toESM(require("@backs/logger"));
var import_config_parser = __toESM(require("@backs/config-parser"));
// src/managers/commands.ts
var import_discord = require("discord.js");
// src/typings/commands.ts
var RunIn = /* @__PURE__ */ ((RunIn2) => {
RunIn2[RunIn2["DM"] = 0] = "DM";
RunIn2[RunIn2["Guild"] = 1] = "Guild";
RunIn2[RunIn2["Any"] = 2] = "Any";
return RunIn2;
})(RunIn || {});
var PermissionsMode = /* @__PURE__ */ ((PermissionsMode2) => {
PermissionsMode2[PermissionsMode2["MatchNone"] = 0] = "MatchNone";
PermissionsMode2[PermissionsMode2["MatchOne"] = 1] = "MatchOne";
PermissionsMode2[PermissionsMode2["MatchAll"] = 2] = "MatchAll";
return PermissionsMode2;
})(PermissionsMode || {});
// src/managers/commands.ts
var import_utils = require("@backs/utils");
var CommandManager = class {
client;
constructor(client) {
this.client = client;
}
stores = {
slash: new import_discord.Collection(),
contextMenuUser: new import_discord.Collection(),
contextMenuMessage: new import_discord.Collection(),
message: new import_discord.Collection(),
selectMenu: new import_discord.Collection(),
button: new import_discord.Collection(),
modal: new import_discord.Collection()
};
ratelimits = {
user: new import_discord.Collection(),
guild: new import_discord.Collection(),
channel: new import_discord.Collection(),
global: new import_discord.Collection()
};
destroy() {
for (const store of Object.values(this.stores)) store.clear();
for (const ratelimit of Object.values(this.ratelimits)) ratelimit.clear();
}
async run(intOrMsg, { commandName, precommand, postcommand, postprocess, postrun, behavior, translations }) {
try {
if (await precommand?.(intOrMsg) === false) return;
if (intOrMsg instanceof import_discord.Message) {
const command = this.stores.message.get(commandName);
if (!command) {
if (behavior === "preferReply") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
]
});
else return;
}
if (await postcommand?.(intOrMsg, command) === false) return;
if (command.enabled === false) {
if (behavior === "preferReply") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
]
});
else return;
}
if (intOrMsg.inGuild() && command.options?.runIn === 0 /* DM */ || !intOrMsg.inGuild() && command.options?.runIn === 1 /* Guild */ || intOrMsg.inGuild() && !this.checkPermissions(command, intOrMsg.member)) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.notallowed?.[intOrMsg.guild?.preferredLocale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "You cannot use this command.").setColor("Red")
]
});
else return;
}
if (this.ratelimit(command, intOrMsg.author.id)) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.ratelimit?.[intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "You're a bit too quick there.").setColor("Red")
]
});
else return;
}
const args = command.data.arguments ? this.parseArgs(intOrMsg.content.slice(intOrMsg.content.indexOf(commandName) - 1 + commandName.length), command.data.arguments) : {};
if (await postprocess?.(intOrMsg, command) === false) return;
const result = await command.run(intOrMsg, args);
if (await postrun?.(intOrMsg, command, result) === false) return;
} else if (intOrMsg.isCommand()) {
let command;
intOrMsg.member;
if (intOrMsg.isChatInputCommand()) command = this.stores.slash.get(intOrMsg.commandName);
else {
if (intOrMsg.isUserContextMenuCommand()) command = this.stores.contextMenuUser.get(intOrMsg.commandName);
else command = this.stores.contextMenuMessage.get(intOrMsg.commandName);
}
if (!command) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (await postcommand?.(intOrMsg, command) === false) return;
if (command.enabled === false) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (intOrMsg.inGuild() && command.options?.runIn === 0 /* DM */ || !intOrMsg.inGuild() && command.options?.runIn === 1 /* Guild */ || intOrMsg.inGuild() && !this.checkPermissions(command, intOrMsg.member)) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.notallowed?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "You cannot use this command.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (this.ratelimit(command, intOrMsg.user.id)) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.ratelimit?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "You're a bit too quick there.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (await postprocess?.(intOrMsg, command) === false) return;
const result = await command.run(intOrMsg);
if (await postrun?.(intOrMsg, command, result) === false) return;
} else if (intOrMsg.isAutocomplete()) {
const command = this.stores.slash.get(intOrMsg.commandName);
if (!command || command.enabled === false || !(intOrMsg.options.getFocused(true).name in (command.autocomplete ?? {}))) return await intOrMsg.respond([]);
if (await postcommand?.(intOrMsg, command) === false) return;
const result = await command.autocomplete[intOrMsg.options.getFocused(true).name](intOrMsg, intOrMsg.options.getFocused());
if (await postrun?.(intOrMsg, command, result) === false) return;
} else {
let command;
const args = this.parseArgs(intOrMsg.customId, command?.arguments ?? []);
if (!args || !args.id) {
if (behavior !== "preferIgnore" || intOrMsg.isModalSubmit()) return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
],
ephemeral: true
});
else return await intOrMsg.update({});
}
if (intOrMsg.isButton() && args.id === "multipageembed") return;
if (intOrMsg.isButton()) command = this.stores.button.get(args.id);
else if (intOrMsg.isAnySelectMenu()) command = this.stores.selectMenu.get(args.id);
else command = this.stores.modal.get(args.id);
if (!command) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (await postcommand?.(intOrMsg, command) === false) return;
if (command.enabled === false) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.unavailable?.[intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "This command is unavailable.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (args.userId && intOrMsg.user.id !== args.userId) {
if (behavior !== "preferIgnore") return await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription(translations?.notallowed?.[intOrMsg.locale ?? intOrMsg.guild?.preferredLocale ?? "en_US"] ?? "You are not allowed to use this command.").setColor("Red")
],
ephemeral: true
});
else return;
}
if (await postprocess?.(intOrMsg, command) === false) return;
const result = await command.run(intOrMsg, args);
if (await postrun?.(intOrMsg, command, result) === false) return;
}
} catch (err) {
this.client.logger.error(err);
if (intOrMsg instanceof import_discord.Message) await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription("An error occurred while executing this command.").setColor(this.client.color("error"))
]
});
else {
if (intOrMsg.isAutocomplete()) {
if (!intOrMsg.responded) await intOrMsg.respond([]).catch(() => {
});
} else if (intOrMsg.replied) await intOrMsg.followUp({
embeds: [
new import_discord.EmbedBuilder().setDescription("An error occurred while executing this command.").setColor(this.client.color("error"))
]
}).catch(() => {
});
else if (intOrMsg.deferred) await intOrMsg.editReply({
embeds: [
new import_discord.EmbedBuilder().setDescription("An error occurred while executing this command.").setColor(this.client.color("error"))
]
}).catch(() => {
});
else await intOrMsg.reply({
embeds: [
new import_discord.EmbedBuilder().setDescription("An error occurred while executing this command.").setColor(this.client.color("error"))
],
ephemeral: true
}).catch(() => {
});
}
}
}
checkPermissions(command, user) {
if (this.client.botOptions.owners?.includes(user.user.id)) return true;
const permissions = user instanceof import_discord.GuildMember ? user.permissions : new import_discord.PermissionsBitField(BigInt(user.permissions));
const restricted = command.options?.restricted ?? false;
if (!command.options?.permissions) return !restricted;
if (command.options.permissions.allow) {
const mode = command.options.permissions.allowMode ?? 1 /* MatchOne */;
if (mode === 1 /* MatchOne */) {
for (const permission of command.options.permissions.allow)
if (permissions.has(permission, true)) return true;
return false;
} else if (mode === 2 /* MatchAll */) {
for (const permission of command.options.permissions.allow)
if (!permissions.has(permission, true)) return false;
return true;
} else {
for (const permission of command.options.permissions.allow)
if (permissions.has(permission, true)) return false;
return true;
}
}
if (command.options.permissions.deny) {
const mode = command.options.permissions.allowMode ?? 1 /* MatchOne */;
if (mode === 1 /* MatchOne */) {
for (const permission of command.options.permissions.deny)
if (permissions.has(permission, true)) return false;
return true;
} else if (mode === 2 /* MatchAll */) {
for (const permission of command.options.permissions.deny)
if (!permissions.has(permission, true)) return true;
return false;
} else {
for (const permission of command.options.permissions.deny)
if (permissions.has(permission, true)) return true;
return false;
}
}
return !restricted;
}
ratelimit(command, userId) {
const ratelimit = command.options?.ratelimit;
if (!ratelimit) return false;
for (const [key, value] of Object.entries(ratelimit)) {
if (value === 0) continue;
const now = Date.now();
const ratelimits = this.ratelimits[key];
if (ratelimits.has(userId)) {
const userRatelimits = ratelimits.get(userId);
ratelimits.set(userId, userRatelimits.filter((r) => r.expires > now));
const commandRatelimit = userRatelimits.find((r) => r.command === command.data.name && r.type === command.commandType);
if (commandRatelimit && commandRatelimit.expires > now) return true;
}
const expires = now + value;
ratelimits.set(userId, [...ratelimits.get(userId) ?? [], { command: command.data.name, type: command.commandType, expires }]);
}
return false;
}
parseArgs(args, format) {
if (Array.isArray(format)) {
const argsArray = args.split("/");
return {
id: argsArray.shift(),
userId: argsArray.shift(),
...argsArray.reduce((o, v, i) => {
o[format[i]] = v;
return o;
}, {})
};
} else if (typeof format === "string") {
const argDefinitions = args.match(/<[^<>\[\]]>|\[[^<>\[\]]\]>/g) ?? [], parsedArgs = {}, content = args.split(/\s+/);
const getNext = (required) => {
const c = content.shift();
if (required && !c) throw new Error("Missing required argument.");
else if (!c) return void 0;
else return c;
};
for (let i = 0; i < argDefinitions.length; i++) {
const arg = argDefinitions[i];
const [name, options] = arg.match(/(?:<|\[)([a-zA-Z0-9_-]+)(:(?:[a-zA-Z0-9_-],? *)+|\((?:.+)\)|\.\.\.)?(?:>|])/) ?? [];
const isRequired = arg.startsWith("<") && arg.endsWith(">");
if (!isRequired && (!arg.startsWith("[") || !arg.endsWith("]"))) throw new Error("Invalid format.");
if (!name) throw new Error("Invalid format.");
if (options) {
if (options.endsWith("...")) {
if (i !== argDefinitions.length - 1) throw new Error("Rest arguments must be the last argument.");
if (content.length === 0) {
if (isRequired) throw new Error("Missing required argument.");
else continue;
}
const c = args.match(new RegExp(`\\s(${(0, import_utils.escape)(content[0])}s+)`))[1];
parsedArgs[name] = c;
} else if (options.startsWith("(") && options.endsWith(")")) {
const regex = new RegExp(options.slice(1, -1));
const c = getNext(isRequired);
if (c && !regex.test(c)) {
if (isRequired) throw new Error("Invalid required argument.");
else {
parsedArgs[name] = void 0;
continue;
}
;
}
parsedArgs[name] = c;
} else if (options.startsWith(":")) {
const enums = options.slice(1).split(",");
const c = getNext(isRequired);
if (c && !enums.includes(c)) {
if (isRequired) throw new Error("Invalid required argument.");
else {
parsedArgs[name] = enums.length === 1 ? false : void 0;
continue;
}
}
parsedArgs[name] = enums.length === 1 ? true : c;
} else throw new Error("Invalid format.");
} else parsedArgs[name] = getNext(isRequired);
}
return parsedArgs;
} else throw new Error("Invalid format.");
}
};
// src/managers/plugins.ts
var import_discord2 = require("discord.js");
var PluginManager = class {
client;
constructor(client) {
this.client = client;
}
store = new import_discord2.Collection();
events = {
prerun: {},
postrun: {}
};
commands = {
precommand: [],
postcommand: [],
postprocess: [],
postrun: []
};
register(plugin) {
this.store.set(plugin.name, plugin);
if (!(plugin.enabled ?? true) || !(this.client.botOptions.enablePlugins ?? true) && plugin.enabled !== true) return;
if (plugin.events) {
for (const [name, options] of Object.entries(plugin.events)) {
if (options.prerun) {
if (!this.events.prerun[name]) this.events.prerun[name] = [];
this.events.prerun[name].push(options.prerun);
}
if (options.postrun) {
if (!this.events.postrun[name]) this.events.postrun[name] = [];
this.events.prerun[name].push(options.postrun);
}
}
}
if (plugin.commands) {
if (plugin.commands.precommand)
this.commands.precommand.push(plugin.commands.precommand);
if (plugin.commands.postcommand)
this.commands.postcommand.push(plugin.commands.postcommand);
if (plugin.commands.postprocess)
this.commands.postprocess.push(plugin.commands.postprocess);
if (plugin.commands.postrun)
this.commands.postrun.push(plugin.commands.postrun);
}
}
};
// src/managers/multipageembed.ts
var import_discord7 = require("discord.js");
// src/structures/multipageembed.ts
var import_discord6 = require("discord.js");
// src/builders/button.ts
var import_discord3 = require("discord.js");
var ButtonBuilder = class extends import_discord3.ButtonBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setStyle(style) {
if (style === "blurple") return super.setStyle(import_discord3.ButtonStyle.Primary);
else if (style === "gray") return super.setStyle(import_discord3.ButtonStyle.Secondary);
else if (style === "green") return super.setStyle(import_discord3.ButtonStyle.Success);
else if (style === "red") return super.setStyle(import_discord3.ButtonStyle.Danger);
else if (style === "link") return super.setStyle(import_discord3.ButtonStyle.Link);
else return super.setStyle(style);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
// src/builders/modal.ts
var import_discord4 = require("discord.js");
var ModalBuilder = class extends import_discord4.ModalBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
// src/builders/selectmenus.ts
var import_discord5 = require("discord.js");
var StringSelectMenuBuilder = class extends import_discord5.StringSelectMenuBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
var UserSelectMenuBuilder = class extends import_discord5.UserSelectMenuBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
var RoleSelectMenuBuilder = class extends import_discord5.RoleSelectMenuBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
var ChannelSelectMenuBuilder = class extends import_discord5.ChannelSelectMenuBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
var MentionableSelectMenuBuilder = class extends import_discord5.MentionableSelectMenuBuilder {
customId;
userId;
arguments;
constructor(data) {
super(data);
}
setCustomId(id) {
this.customId = id;
const customId = [this.customId, this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setUserId(id) {
this.userId = id;
const customId = [this.customId ?? "", this.userId ?? "", ...this.arguments ?? []].join("/");
return super.setCustomId(customId);
}
setArguments(...args) {
this.arguments = args;
args.unshift(this.userId ?? "");
args.unshift(this.customId ?? "");
return super.setCustomId(args.join("/"));
}
};
// src/structures/multipageembed.ts
var MultiPageEmbed = class {
_id;
_userId;
_expires;
_startingPage;
_embeds;
_canLoop;
_jump;
get id() {
return this._id;
}
get userId() {
return this._userId;
}
get expires() {
return this._expires;
}
get startingPage() {
return this._startingPage;
}
get embeds() {
return this._embeds;
}
get canLoop() {
return this._canLoop;
}
get jump() {
return this._jump;
}
constructor(data) {
this._id = data.id;
this._expires = data.expires ?? 12e4;
this._startingPage = data.startingPage ?? 0;
this._embeds = data.embeds ?? [];
this._canLoop = data.canLoop ?? true;
this._jump = data.jump ?? false;
}
get components() {
const row = new import_discord6.ActionRowBuilder();
if (this.jump) row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(this.userId).setArguments("jump-start", this.id).setEmoji("\u23EE\uFE0F").setStyle("gray").setDisabled(this.startingPage === 0)
);
row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(this.userId).setArguments("previous", this.id).setEmoji("\u23EA").setStyle("gray").setDisabled(this.startingPage === 0 && !this.canLoop),
new ButtonBuilder().setCustomId("multipageembed").setUserId(this.userId).setArguments("next", this.id).setEmoji("\u23E9").setStyle("gray").setDisabled(this.startingPage === this.embeds.length - 1 && !this.canLoop)
);
if (this.jump) row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(this.userId).setArguments("jump-end", this.id).setEmoji("\u23ED\uFE0F").setStyle("gray").setDisabled(this.startingPage === this.embeds.length - 1)
);
return [row];
}
get page() {
return this.embeds[this.startingPage];
}
};
// src/managers/multipageembed.ts
var import_utils2 = require("@backs/utils");
var MultiPageEmbedManager = class {
client;
constructor(client) {
this.client = client;
this.client.on("interactionCreate", async (interaction) => {
if (interaction.isButton()) {
const args = this.client.commands.parseArgs(interaction.customId, ["command", "embedId"]);
if (!args.command || !args.embedId || args.userId && args.userId !== interaction.user.id) return await interaction.update({});
if (args.id === "multipageembed") {
await interaction.deferUpdate();
const shouldUpdate = this.handle(args.embedId, args.command);
if (shouldUpdate === true) {
const handler = this.handlers.get(args.embedId);
const embed = handler.embed.embeds[handler.index];
const row = new import_discord7.ActionRowBuilder();
if (handler.embed.jump) row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(handler.userId).setArguments("jump-start", args.embedId).setEmoji("\u23EE\uFE0F").setStyle("gray").setDisabled(handler.index === 0)
);
row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(handler.userId).setArguments("previous", args.embedId).setEmoji("\u23EA").setStyle("gray").setDisabled(handler.index === 0 && !handler.embed.canLoop),
new ButtonBuilder().setCustomId("multipageembed").setUserId(handler.userId).setArguments("next", args.embedId).setEmoji("\u23E9").setStyle("gray").setDisabled(handler.index === handler.embed.embeds.length - 1 && !handler.embed.canLoop)
);
if (handler.embed.jump) row.addComponents(
new ButtonBuilder().setCustomId("multipageembed").setUserId(handler.userId).setArguments("jump-end", args.embedId).setEmoji("\u23ED\uFE0F").setStyle("gray").setDisabled(handler.index === handler.embed.embeds.length - 1)
);
await interaction.editReply({
embeds: [embed, ...interaction.message.embeds.slice(1)],
components: [row, ...interaction.message.components.slice(1)]
});
} else if (shouldUpdate === false) {
await interaction.editReply({});
} else {
await interaction.editReply({
components: [...interaction.message.components.slice(1)]
});
}
}
}
});
}
embeds = new import_discord7.Collection();
handlers = new import_discord7.Collection();
create(data, createEmbed = true, userId) {
const embedId = data.id ?? (0, import_utils2.uuid)();
this.embeds.set(embedId, {
...data,
id: void 0
});
const id = (0, import_utils2.uuid)();
if (!createEmbed) return;
const embed = new MultiPageEmbed({
...data,
id
});
const expires = embed.expires;
this.handlers.set(id, {
embed,
index: data.startingPage ?? 0,
expires: Date.now() + expires,
userId
});
setTimeout(() => this.handlers.delete(id), expires);
return embed;
}
get(embedId, userId) {
const embedData = this.embeds.get(embedId);
if (!embedData) return;
const id = (0, import_utils2.uuid)();
const embed = new MultiPageEmbed({
...embedData,
id
});
const expires = embed.expires;
this.handlers.set(id, {
embed,
index: embedData.startingPage ?? 0,
expires: Date.now() + expires,
userId
});
setTimeout(() => this.handlers.delete(id), expires);
return embed;
}
delete(embedId) {
this.embeds.delete(embedId);
}
handle(embedId, command) {
const handler = this.handlers.get(embedId);
if (!handler) return;
switch (command) {
case "jump-start":
if (!handler.embed.jump) return false;
handler.index = 0;
break;
case "previous":
if (handler.index === 0) {
if (handler.embed.canLoop) handler.index = handler.embed.embeds.length - 1;
} else handler.index--;
break;
case "next":
if (handler.index === handler.embed.embeds.length - 1) {
if (handler.embed.canLoop) handler.index = 0;
} else handler.index++;
break;
case "jump-end":
if (!handler.embed.jump) return false;
handler.index = handler.embed.embeds.length - 1;
break;
}
return true;
}
};
// src/structures/commands.ts
var MessageCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType = "message";
data;
run;
enabled;
options;
isIDBased() {
return false;
}
isSlashCommand() {
return false;
}
isContextMenuCommand() {
return false;
}
isMessageCommand() {
return true;
}
};
var SlashCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType = "slash";
data;
run;
autocomplete;
enabled;
options;
isIDBased() {
return false;
}
isSlashCommand() {
return true;
}
isContextMenuCommand() {
return false;
}
isMessageCommand() {
return false;
}
};
var ContextMenuCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType;
data;
type;
run;
enabled;
options;
isIDBased() {
return false;
}
isSlashCommand() {
return false;
}
isContextMenuCommand() {
return true;
}
isUserContextMenuCommand() {
return this.type === "user";
}
isMessageContextMenuCommand() {
return this.type === "message";
}
isMessageCommand() {
return false;
}
};
var SelectMenuCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType = "selectMenu";
run;
id;
type;
user;
arguments;
enabled;
options;
isIDBased() {
return true;
}
isSlashCommand() {
return false;
}
isContextMenuCommand() {
return false;
}
isMessageCommand() {
return false;
}
isSelectMenuCommand() {
return true;
}
isButtonCommand() {
return false;
}
isModalCommand() {
return false;
}
isStringSelectMenuCommand() {
return this.type === "string";
}
isUserSelectMenuCommand() {
return this.type === "user";
}
isChannelSelectMenuCommand() {
return this.type === "channel";
}
isRoleSelectMenuCommand() {
return this.type === "role";
}
isMentionableSelectMenuCommand() {
return this.type === "mentionable";
}
};
var ButtonCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType = "button";
run;
id;
user;
arguments;
enabled;
options;
isIDBased() {
return true;
}
isSlashCommand() {
return false;
}
isContextMenuCommand() {
return false;
}
isMessageCommand() {
return false;
}
isSelectMenuCommand() {
return false;
}
isButtonCommand() {
return true;
}
isModalCommand() {
return false;
}
};
var ModalCommand = class {
constructor(options) {
Object.assign(this, options);
}
commandType = "modal";
run;
id;
user;
arguments;
enabled;
options;
isIDBased() {
return true;
}
isSlashCommand() {
return false;
}
isContextMenuCommand() {
return false;
}
isMessageCommand() {
return false;
}
isSelectMenuCommand() {
return false;
}
isButtonCommand() {
return false;
}
isModalCommand() {
return true;
}
};
// src/structures/events.ts
var Event = class {
constructor(options) {
Object.assign(this, options);
}
name;
readableName;
enabled;
once;
run;
};
// src/structures/plugins.ts
var Plugin = class {
constructor(options) {
Object.assign(this, options);
}
name;
description;
enabled;
version;
events;
commands;
};
// src/index.ts
async function createClient(options) {
const configParser = new import_config_parser.default({
...options.configOptions,
logging: {
debug: process.env.NODE_ENV === "debug"
},
start: false
});
await configParser.start();
const parsedOptions = parseOptions(configParser.configs, options);
return new Bot2(parsedOptions, configParser, options);
}
var Bot2 = class extends import_discord8.Client {
configParser;
botOptions;
_botOptions;
logger;
commands;
plugins;
multiPageEmbed;
get configs() {
return this.configParser.configs;
}
get clientEmojis() {
const emojis = this._botOptions.emojis;
if (typeof emojis === "function") return emojis(this.configs);
else return this.botOptions.emojis ?? {};
}
get clientColors() {
const colors2 = this._botOptions.colors;
if (typeof colors2 === "function") return colors2(this.configs);
else return this.botOptions.colors ?? {};
}
constructor(options, configParser, BotBaseOptions) {
options.clientOptions.ws ??= {};
options.clientOptions.ws.properties ??= {};
options.clientOptions.ws.properties.$browser = options.clientOptions.browser;
super(options.clientOptions);
this.configParser = configParser;
this.botOptions = options;
this._botOptions = BotBaseOptions;
this.botOptions.debug ??= process.env.NODE_ENV === "debug";
this.botOptions.loggerOptions ??= {};
this.botOptions.loggerOptions.log ??= {};
this.botOptions.loggerOptions.log.debug ??= this.botOptions.debug;
this.logger = (0, import_logger.default)(this.botOptions.loggerOptions);
this.commands = new CommandManager(this);
this.plugins = new PluginManager(this);
this.multiPageEmbed = new MultiPageEmbedManager(this);
process.on("uncaughtException", (err) => {
this.logger.error(err);
});
process.on("unhandledRejection", (err, promise) => {
this.logger.error(err);
promise.catch((err2) => this.logger.error(err2));
});
}
emoji(name) {
return this.clientEmojis[name];
}
color(name) {
return this.clientColors[name];
}
async login(token) {
if (!this.configParser.isStarted) await this.configParser.start();
token = token ?? process.env.TOKEN ?? process.env.DISCORD_TOKEN ?? process.env.BOT_TOKEN ?? process.env.DISCORD_BOT_TOKEN ?? this.botOptions.token;
await this.loadPlugins();
await this.loadEvents();
await this.loadCommands();
if (this.botOptions.debug) this.on("debug", (info) => this.logger.debug((0, import_utils3.highlight)(info, magenta)));
this.once("ready", () => this.logger.log("Client logged in as %bl_i.", `@${this.user.tag}`));
const loggedIn = await super.login(token).catch((err) => {
this.logger.error("Failed to login to Discord. %r", "Exiting...");
this.logger.error(err);
process.exit(1);
});
await this.application?.fetch();
this.logger.debug(
`Logged in.
%m ${this.guilds.cache.size}
%m ${this.ws.shards.size}`,
"Guilds:",
"Shards:"
);
return loggedIn;
}
async destroy() {
this.logger.debug("Stopping config parser...");
this.configParser.stop();
this.logger.debug("Clearing commands...");
this.commands.destroy();
this.logger.debug("Destroying client...");
await super.destroy();
}
async load(filePath) {
if ((0, import_is_glob.default)(filePath)) {
const globbedFiles = await (0, import_glob.glob)(filePath);
const toLoad = [];
const files = {};
const loaded = [];
for (let file of globbedFiles) {
if (!import_path.default.isAbsolute(file)) file = import_path.default.join(import_path.default.relative(__dirname, process.cwd()), file);
if (import_path.default.extname(file) !== ".ts" && import_utils3.isTypescript || import_path.default.extname(file) !== ".js" && !import_utils3.isTypescript) {
files[file] = {
status: "skipped",
reason: import_utils3.isTypescript ? "JS file in TS environment" : "TS file in JS environment"
};
continue;
}
toLoad.push(file);
}
for (const file of toLoad) {
try {
const loadedFiles = require(file).default;
loaded.push(loadedFiles);
files[file] = {
status: "loaded",
index: loaded.length - 1
};
} catch (err) {
this.logger.error(err);
files[file] = {
status: "errored",
reason: (0, import_utils3.crop)(err.message, 50)
};
}
}
return {
loaded,
files
};
} else {
if (!import_path.default.isAbsolute(filePath)) filePath = import_path.default.relative(__dirname, import_path.default.join(process.cwd(), filePath));
if (import_path.default.extname(filePath) !== ".ts" && import_utils3.isTypescript || import_path.default.extname(filePath) !== ".js" && !import_utils3.isTypescript)
return {
loaded: [],
files: {
[filePath]: {
status: "skipped",
reason: import_utils3.isTypescript ? "JS file in TS environment" : "TS file in JS environment"
}
}
};
try {
const file = require(filePath).default;
return {
loaded: [file],
files: {
[filePath]: {
status: "loaded",
index: 0
}
}
};
} catch (err) {
this.logger.error(err);
return {
loaded: [],
files: {
[filePath]: {
status: "errored",
reason: err.message
}
}
};
}
}
}
async loadCommands() {
const commands = await Promise.all(
this.botOptions.commands.map(async (command) => await this.load(command))
);
const loadedCommands = commands.flatMap((e) => e.loaded);
const loadedCommandsData = [], commandFiles = commands.flatMap((e) => Object.entries(e.files));
for (let i = 0; i < loadedCommands.length; i++) {
try {
const command = loadedCommands[i], filePath = commandFiles.find((e) => e[1].index === i)[0];
if (command.isIDBased()) {
if (command.isSelectMenuCommand()) this.commands.stores.selectMenu.set(command.id, command);
else if (command.isButtonCommand()) this.commands.stores.button.set(command.id, command);
else if (command.isModalCommand()) this.commands.stores.modal.set(command.id, command);
loadedCommandsData.push({
name: command.id,
type: command.isSelectMenuCommand() ? "Select Menu" : command.isButtonCommand() ? "Button" : "Modal",
status: command.enabled ? green("Enabled") : red("Disabled"),
loadStatus: bgGreen(`${commandFiles[i][1].status}
${commandFiles[i][1].reason ?? ""}`),
filePath
});
} else {
if (command.isSlashCommand()) this.commands.stores.slash.set(command.data.name, command);
else if (command.isContextMenuCommand()) {
if (command.isUserContextMenuCommand())
this.commands.stores.contextMenuUser.set(command.data.name, command);
else this.commands.stores.contextMenuMessage.set(command.data.name, command);
} else if (command.isMessageCommand()) {
this.commands.stores.message.set(command.data.name, command);
for (const name of Object.values(command.data.nameLocalizations ?? {}))
this.commands.stores.message.set(name, command);
for (const alias of command.data.aliases ?? [])
this.commands.stores.message.set(alias, command);
}
loadedCommandsData.push({
name: command.data.name,
type: command.isSlashCommand() ? "Slash" : command.isContextMenuCommand() ? "Context Menu" : "Message",
status: command.enabled ?? true ? green("Enabled") : red("Disabled"),
loadStatus: bgGreen(`${commandFiles[i][1].status}
${commandFiles[i][1].reason ?? ""}`),
filePath
});
}
} catch (err) {
this.logger.error(err);
loadedCommandsData.push({
name: import_path.default.basename(commandFiles[i][0], import_path.default.extname(commandFiles[i][0])),
type: italic("Unknown"),
status: red("errored"),
loadStatus: bgRed(`${commandFiles[i][1].status}
${commandFiles[i][1].reason ?? ""}`),
filePath: commandFiles[i][0]
});
}
}
for (const [filePath, command] of commandFiles) {
if (command.status === "loaded") continue;
loadedCommandsData.push({
name: import_path.default.basename(filePath, import_path.default.extname(filePath)),
type: italic("Unknown"),
status: blackBright("Unknown"),
loadStatus: command.status === "skipped" ? bgYellow(`${command.status}
${command.reason ?? ""}`) : bgRed(`${command.status}
${command.reason ?? ""}`),
filePath
});
}
if (this.botOptions.debug) {
this.logger.debug(" ");
this.logger.table(
[
["Command Name", "Type", "Status", "Load Status", "File Path"].map((h) => bold(h)),
...loadedCommandsData.map((e) => [e.name, e.type, e.status, e.loadStatus, e.filePath]),
[
bold("Total"),
...Object.entries(
Object.values(commands).reduce(
(acc, e) => {
Object.values(e.files).forEach((e2) => acc[e2.status]++);
return acc;
},
{ loaded: 0, skipped: 0, errored: 0 }
)
).map(([k, v]) => `${k === "loaded" ? green(k) : k === "skipped" ? yellow(k) : red(k)}: ${v}`),
""
]
],
{
header: {
content: magenta("Loaded Commands")
}
}
);
}
}
async loadEvents() {
const events = await Promise.all(
this.botOptions.events.map(async (event) => await this.load(event))
);
const loadedEvents = events.flatMap((e) => e.loaded);
for (const event of loadedEvents) {
try {
if (!(event.enabled ??= true)) continue;
if (event.once)
this.once(event.name, async (...args) => {
for (const prerun of this.plugins.events[event.name]?.prerun ?? []) await prerun(...args);
const result = await event.run.bind(event)(...args);
for (const postrun of this.plugins.events[event.name]?.postrun ?? [])
await postrun(result, ...args);
});
else
this.on(event.name, async (...args) => {
for (const prerun of this.plugins.events[event.name]?.prerun ?? []) await prerun(...args);
const