supabotbase
Version:
An easy-to-use Discord.js Bot base with messages and interactions support.
305 lines (234 loc) • 10.6 kB
JavaScript
const { MessageEmbed } = require("discord.js");
module.exports = class MessageHandler {
getEvent() {
return "message";
}
async onExecute(message) {
if (this.main.config.disable && this.main.config.disable.message) return;
if (message.author.bot) return;
this.addSlashCommandSupportData(message);
if (this.main.database && this.main.config.database) this.loadSettings(message);
this.setMessageFunctions(message);
if (!await this.checkMessageHandles(message)) return;
let prefix = this.getFullPrefix(message);
message.prefix = prefix;
if (!message.content.toLowerCase().startsWith(prefix)) return;
let {args, command} = this.getMessageAndArgs(message.content, prefix);
let cmdFile = this.getCommand(command);
if (!cmdFile) return;
if (cmdFile.args.length !== 0) {
let example = this.getCommandExample(cmdFile, prefix);
let usage = this.parseCommandExample(cmdFile.help.usage, prefix, cmdFile.help.name);
message._argAnswerSent = false;
args = await this.checkValidArgs(cmdFile.args, args, message, example, message.guild, usage);
if (!args) return;
}
if (cmdFile.security && !(await this.checkSecurity(cmdFile, message, args))) return;
this.client.emit("commandExecute", cmdFile, message);
if (!this.main.errorHandler) return cmdFile.onExecute(message, args);
if (cmdFile.onExecute.constructor.name === "AsyncFunction") return cmdFile.onExecute(message, args).catch((e) => {
this.main.errorHandler._onMessageError(e, message, cmdFile);
});
try {
cmdFile.onExecute(message, args);
} catch(e) {
this.main.errorHandler._onMessageError(e, message, cmdFile);
}
}
async checkSecurity(cmdFile, message, args) {
if (!cmdFile.security) return true;
if (cmdFile.security.requiredPermissions && !(await this.checkRequiredPermissions(cmdFile.security.requiredPermissions, message))) return false;
if (cmdFile.security.restriction && !(this.checkAllRestrictions(cmdFile.security.restriction, message))) return false;
if (cmdFile.security.checks && !(this.individualChecks(cmdFile.security.checks, message, args))) return false;
return true;
}
individualChecks(checks, message, args) {
let confirm = true;
for (let check of checks) {
if (!this.main.permissionChecks[check]) continue;
let res = this.main.permissionChecks[check](message, args);
if (res === false) {
confirm = false;
break;
}
if (typeof res === "string" || res instanceof MessageEmbed) {
message.channel.send(res);
confirm = false;
break;
}
}
return confirm;
}
checkAllRestrictions(restrictions, message) {
if (restrictions.user && !this.checkRestriction(restrictions.user, message.author.id)) return false;
if (restrictions.channel && !this.checkRestriction(restrictions.channel, message.channel.id)) return false;
if (restrictions.guild && !this.checkRestriction(restrictions.guild, message.guild.id)) return false;
return true;
}
checkRestriction(restriction, id) {
if (restriction[0] === "specific") {
if (restriction[1] !== id && !restriction[1].includes(id)) return false;
return true;
}
let data = this.main.database.get(restriction[1]);
if (data && typeof data === "object" && !data.includes(id)) return false;
return true;
}
async checkRequiredPermissions({bot = [], user = []}, message) {
let missing = this.getMissingPermissions(bot, await message.guild.members.fetch(this.client.user.id), message.channel);
if (missing.length > 0) {
message.channel.send(this.main.config.messages.botNoPermissions
.replace("{0}", `\n- ${missing.join("\n- ")}`) );
return false;
}
missing = this.getMissingPermissions(user, message.member, message.channel);
if (missing.length > 0) {
message.channel.send(this.main.config.messages.userNoPermissions
.replace("{0}", `\n- ${missing.join("\n- ")}`));
return false;
}
return true;
}
getMissingPermissions(list, member, channel) {
let res = [];
let permissions = member.permissionsIn(channel).serialize();
list.forEach((perm) => {
if (!permissions[perm]) res.push(perm);
});
return res;
}
async checkValidArgs(cmdArgs, user, message, example, guild, usage, onlyFirst = false) {
let parsed = [];
let curUserArg = user.shift();
if (!cmdArgs[0]) return [];
let failed = false;
if (curUserArg) {
let res = false;
for (let i of cmdArgs) {
if (![1, 2].includes(i.type) || i.name.toLowerCase() !== curUserArg.toLowerCase()) continue;
parsed.push(i.name.toLowerCase());
let index = cmdArgs.indexOf(i);
cmdArgs.splice(index, 1);
if (![1, 2].includes(i.options[0].type)) onlyFirst = true;
res = await this.checkValidArgs(i.options, user, message, example, guild, usage, onlyFirst);
cmdArgs.splice(index, 0, i);
if (!res) return false;
break;
}
if (res) parsed.push(...res);
else if (![1, 2].includes(cmdArgs[0].type)) {
let arg = cmdArgs.shift();
if (arg.type === 3) parsed.push(curUserArg);
else if (arg.type === 4) {
let n = Number(curUserArg);
if (isNaN(n)) failed = true;
else parsed.push(n);
} else if (arg.type === 5) {
let out = undefined;
if (curUserArg === "true" || curUserArg === "on" || curUserArg === "yes") out = true;
else if (curUserArg === "false" || curUserArg === "off" || curUserArg === "no") out = false;
if (out === undefined) failed = true;
else parsed.push(out);
} else {
let toFetch = curUserArg;
if (isNaN(Number(toFetch))) {
if (!toFetch.startsWith("<") || !toFetch.endsWith(">")) failed = true;
else toFetch = toFetch.split("<").join("").split(">").join("").split("@").join("").split("&").join("").split("#").join("").split("!").join("");
}
let out = false;
if (!failed && arg.type === 6) out = await message.guild.members.fetch(toFetch).catch(_e => {/* Nothing */});
else if (!failed && arg.type === 7) out = message.guild.channels.cache.get(toFetch);
else if (!failed && arg.type === 8) out = message.guild.roles.cache.get(toFetch);
if (!out) failed = true;
else parsed.push(out);
}
if (cmdArgs[0]) {
let res = await this.checkValidArgs(cmdArgs, user, message, example, guild, usage);
if (!res) {
cmdArgs.unshift(arg);
return false;
};
parsed.push(...res);
}
cmdArgs.unshift(arg);
}
} else if (([1, 2].includes(cmdArgs[0].type) || cmdArgs[0].required)) failed = true;
if (failed) {
let requiredSubs = [];
let requiredOthers = [];
let typeToName = this.main.config.messages.types;
cmdArgs.forEach((a) => {
if (a.type === 1 || a.type === 2) requiredSubs.push(`**${a.name}**\n> _${a.description}_\n`);
else requiredOthers.push(`**${a.name}** (${this.main.config.messages.needsToBe.replace("{0}", typeToName[a.type])})\n> _${a.description}_\n`);
});
if (onlyFirst) requiredOthers = [requiredOthers[0]];
if (requiredSubs.length > 0) requiredSubs = this.main.config.messages.chooseBetweenSubcommands + `\n> ${requiredSubs.join("\n> ")}`;
else requiredSubs = "";
if (requiredOthers.length > 0) {
if (requiredSubs) requiredOthers = `\n${this.main.config.messages.orOtherOptions}\n> ${requiredOthers.join("\n> ")}`;
else requiredOthers = `${this.main.config.messages.chooseBetweenOptions}\n> ${requiredOthers.join("\n> ")}`
} else requiredOthers = "";
message.channel.send(message.embed()
.setTitle(this.main.config.messages.wrongArguments)
.setDescription(`${this.main.config.messages.usage}: \`${usage}\`\n\n${requiredSubs}${requiredOthers}`)
.setFooter(`${this.main.config.messages.example}: ${example}`)
);
return false;
}
return parsed;
}
mentionToID(i) {
let res = i.split("<@").join("").split(">").join("").split("!").join("").split("&").join("").split("#").join("");
return isNaN(Number(res)) ? false : res;
}
parseCommandExample(string, prefix, name) {
return string.replace("%PREFIX%", prefix).replace("%CMD%", name)
}
getCommandExample(cmdFile, prefix) {
let example = cmdFile.examples[Math.floor(Math.random()*cmdFile.examples.length)];
return this.parseCommandExample(example, prefix, cmdFile.help.name);
}
async checkMessageHandles(message) {
for (let handler of this.main._messageHandlers) {
let res = await handler(message);
if (typeof res === "boolean" && !res) return false;
}
return true;
}
setMessageFunctions(message) {
for (let name in this.main._messageFunctions) {
message[name] = this.main._messageFunctions[name];
}
}
addSlashCommandSupportData(message) {
message.isSlashCommand = false;
message.answerCommand = (content) => {
return message.channel.send(content);
}
}
getCommand(command) {
return this.main.commands.get(command) || this.main.aliases.get(command);
}
getMessageAndArgs(content, prefix) {
let args = content.slice(prefix.length).trim().split(/ +/g);
let command = args.shift().toLowerCase();
return {args, command};
}
loadSettings(message) {
message.guild.settings = this.main.database.get(`guild-${message.guild.id}`);
if (!message.guild.settings) message.guild.settings = this.main.config.database.defaultGuildSettings || {};
message.author.settings = this.main.database.get(`user-${message.author.id}`);
if (!message.author.settings) message.author.settings = this.main.config.database.defaultUserSettings || {};
}
getFullPrefix(message) {
if (this.main.config.disable && this.main.config.disable.mentionPrefix) return this.getPrefix(message);
let prefixMention = new RegExp(`^<@!?${this.client.user.id}> `);
let match = message.content.match(prefixMention);
return match ? match[0] : this.getPrefix(message);
}
getPrefix(message) {
if (this.main.database && message.guild.settings.prefix) return message.guild.settings.prefix;
if (this.main.config.prefix) return this.main.config.prefix;
return ".";
}
}