first commit

This commit is contained in:
Raven Scott
2022-01-24 19:05:30 +00:00
commit 390fff9c42
31 changed files with 10171 additions and 0 deletions

106
commands/conf.js Normal file
View File

@ -0,0 +1,106 @@
/*
FOR GUILD SETTINGS SEE set.js !
This command is used to modify the bot's default configuration values, which affects all guilds.
If a default setting is not specifically overwritten by a guild, changing a default here will
change it for that guild. The `add` action adds a key to the configuration of every guild in
your bot. The `del` action removes the key also from every guild, and loses its value forever.
*/
const { codeBlock } = require("@discordjs/builders");
const config = require("../config.js");
const { awaitReply } = require("../modules/functions.js");
const { settings } = require("../modules/settings.js");
exports.run = async (client, message, [action, key, ...value], level) => { // eslint-disable-line no-unused-vars
// Retrieve Default Values from the default settings in the bot.
const defaults = settings.get("default");
const replying = settings.ensure(message.guild.id, config.defaultSettings).commandReply;
// Adding a new key adds it to every guild (it will be visible to all of them)
if (action === "add") {
if (!key) return message.reply({ content: "Please specify a key to add", allowedMentions: { repliedUser: (replying === "true") }});
if (defaults[key]) return message.reply({ content: "This key already exists in the default settings", allowedMentions: { repliedUser: (replying === "true") }});
if (value.length < 1) return message.reply({ content: "Please specify a value", allowedMentions: { repliedUser: (replying === "true") }});
// `value` being an array, we need to join it first.
defaults[key] = value.join(" ");
// One the settings is modified, we write it back to the collection
settings.set("default", defaults);
message.reply({ content: `${key} successfully added with the value of ${value.join(" ")}`, allowedMentions: { repliedUser: (replying === "true") }});
} else
// Changing the default value of a key only modified it for guilds that did not change it to another value.
if (action === "edit") {
if (!key) return message.reply({ content: "Please specify a key to edit", allowedMentions: { repliedUser: (replying === "true") }});
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
if (value.length < 1) return message.reply({ content: "Please specify a new value", allowedMentions: { repliedUser: (replying === "true") }});
defaults[key] = value.join(" ");
settings.set("default", defaults);
message.reply({ content: `${key} successfully edited to ${value.join(" ")}`, allowedMentions: { repliedUser: (replying === "true") }});
} else
// WARNING: DELETING A KEY FROM THE DEFAULTS ALSO REMOVES IT FROM EVERY GUILD
// MAKE SURE THAT KEY IS REALLY NO LONGER NEEDED!
if (action === "del") {
if (!key) return message.reply({ content: "Please specify a key to delete.", allowedMentions: { repliedUser: (replying === "true") }});
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
// Throw the 'are you sure?' text at them.
const response = await awaitReply(message, `Are you sure you want to permanently delete ${key} from all guilds? This **CANNOT** be undone.`);
// If they respond with y or yes, continue.
if (["y", "yes"].includes(response)) {
// We delete the default `key` here.
delete defaults[key];
settings.set("default", defaults);
// then we loop on all the guilds and remove this key if it exists.
// "if it exists" is done with the filter (if the key is present and it's not the default config!)
for (const [guildId, conf] of settings.filter((setting, id) => setting[key] && id !== "default")) {
delete conf[key];
settings.set(guildId, conf);
}
message.reply({ content: `${key} was successfully deleted.`, allowedMentions: { repliedUser: (replying === "true") }});
} else
// If they respond with n or no, we inform them that the action has been cancelled.
if (["n","no","cancel"].includes(response)) {
message.reply({ content: "Action cancelled.", allowedMentions: { repliedUser: (replying === "true") }});
}
} else
// Display a key's default value
if (action === "get") {
if (!key) return message.reply({ content: "Please specify a key to view", allowedMentions: { repliedUser: (replying === "true") }});
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
message.reply({ content: `The value of ${key} is currently ${defaults[key]}`, allowedMentions: { repliedUser: (replying === "true") }});
// Display all default settings.
} else {
const array = [];
Object.entries(settings.get("default")).forEach(([key, value]) => {
array.push(`${key}${" ".repeat(20 - key.length)}:: ${value}`);
});
await message.channel.send(codeBlock("asciidoc", `= Bot Default Settings =
${array.join("\n")}`));
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["defaults"],
permLevel: "Bot Admin"
};
exports.help = {
name: "conf",
category: "System",
description: "Modify the default configuration for all guilds.",
usage: "conf <view/get/edit> <key> <value>"
};

33
commands/deploy.js Normal file
View File

@ -0,0 +1,33 @@
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
// We'll partition the slash commands based on the guildOnly boolean.
// Separating them into the correct objects defined in the array below.
const [globalCmds, guildCmds] = client.container.slashcmds.partition(c => !c.conf.guildOnly);
// Give the user a notification the commands are deploying.
await message.channel.send("Deploying commands!");
// We'll use set but please keep in mind that `set` is overkill for a singular command.
// Set the guild commands like
await client.guilds.cache.get(message.guild.id)?.commands.set(guildCmds.map(c => c.commandData));
// Then set the global commands like
await client.application?.commands.set(globalCmds.map(c => c.commandData)).catch(e => console.log(e));
// Reply to the user that the commands have been deployed.
await message.channel.send("All commands deployed!");
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "deploy",
category: "System",
description: "This will deploy all slash commands.",
usage: "deploy"
};

52
commands/eval.js Normal file
View File

@ -0,0 +1,52 @@
// The EVAL command will execute **ANY** arbitrary javascript code given to it.
// THIS IS PERMISSION LEVEL 10 FOR A REASON! It's perm level 10 because eval
// can be used to do **anything** on your machine, from stealing information to
// purging the hard drive. DO NOT LET ANYONE ELSE USE THIS
const { codeBlock } = require("@discordjs/builders");
/*
MESSAGE CLEAN FUNCTION
"Clean" removes @everyone pings, as well as tokens, and makes code blocks
escaped so they're shown more easily. As a bonus it resolves promises
and stringifies objects!
This is mostly only used by the Eval and Exec commands.
*/
async function clean(client, text) {
if (text && text.constructor.name == "Promise")
text = await text;
if (typeof text !== "string")
text = require("util").inspect(text, {depth: 1});
text = text
.replace(/`/g, "`" + String.fromCharCode(8203))
.replace(/@/g, "@" + String.fromCharCode(8203));
text = text.replaceAll(client.token, "[REDACTED]");
return text;
}
// However it's, like, super ultra useful for troubleshooting and doing stuff
// you don't want to put in a command.
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
const code = args.join(" ");
const evaled = eval(code);
const cleaned = await clean(client, evaled);
message.channel.send(codeBlock("js", cleaned));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "eval",
category: "System",
description: "Evaluates arbitrary javascript.",
usage: "eval [...code]"
};

69
commands/help.js Normal file
View File

@ -0,0 +1,69 @@
/*
The HELP command is used to display every command's name and description
to the user, so that he may see what commands are available. The help
command is also filtered by level, so if a user does not have access to
a command, it is not shown to them. If a command name is given with the
help command, its extended help is shown.
*/
const { codeBlock } = require("@discordjs/builders");
const { toProperCase } = require("../modules/functions.js");
exports.run = (client, message, args, level) => {
// Grab the container from the client to reduce line length.
const { container } = client;
// If no specific command is called, show all filtered commands.
if (!args[0]) {
// Load guild settings (for prefixes and eventually per-guild tweaks)
const settings = message.settings;
// Filter all commands by which are available for the user's level, using the <Collection>.filter() method.
const myCommands = message.guild ? container.commands.filter(cmd => container.levelCache[cmd.conf.permLevel] <= level) :
container.commands.filter(cmd => container.levelCache[cmd.conf.permLevel] <= level && cmd.conf.guildOnly !== true);
// Then we will filter the myCommands collection again to get the enabled commands.
const enabledCommands = myCommands.filter(cmd => cmd.conf.enabled);
// Here we have to get the command names only, and we use that array to get the longest name.
const commandNames = [...enabledCommands.keys()];
// This make the help commands "aligned" in the output.
const longest = commandNames.reduce((long, str) => Math.max(long, str.length), 0);
let currentCategory = "";
let output = `= Command List =\n[Use ${settings.prefix}help <commandname> for details]\n`;
const sorted = enabledCommands.sort((p, c) => p.help.category > c.help.category ? 1 :
p.help.name > c.help.name && p.help.category === c.help.category ? 1 : -1 );
sorted.forEach( c => {
const cat = toProperCase(c.help.category);
if (currentCategory !== cat) {
output += `\u200b\n== ${cat} ==\n`;
currentCategory = cat;
}
output += `${settings.prefix}${c.help.name}${" ".repeat(longest - c.help.name.length)} :: ${c.help.description}\n`;
});
message.channel.send(codeBlock("asciidoc", output));
} else {
// Show individual command's help.
let command = args[0];
if (container.commands.has(command) || container.commands.has(container.aliases.get(command))) {
command = container.commands.get(command) ?? container.commands.get(container.aliases.get(command));
if (level < container.levelCache[command.conf.permLevel]) return;
message.channel.send(codeBlock("asciidoc", `= ${command.help.name} = \n${command.help.description}\nusage:: ${command.help.usage}\naliases:: ${command.conf.aliases.join(", ")}`));
} else return message.channel.send("No command with that name, or alias exists.");
}};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["h", "halp"],
permLevel: "User"
};
exports.help = {
name: "help",
category: "System",
description: "Displays all the available commands for your permission level.",
usage: "help [command]"
};

21
commands/mylevel.js Normal file
View File

@ -0,0 +1,21 @@
const config = require("../config.js");
const { settings } = require("../modules/settings.js");
exports.run = async (client, message, args, level) => {
const friendly = config.permLevels.find(l => l.level === level).name;
const replying = settings.ensure(message.guild.id, config.defaultSettings).commandReply;
message.reply({ content: `Your permission level is: ${level} - ${friendly}`, allowedMentions: { repliedUser: (replying === "true") }});
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "mylevel",
category: "Miscellaneous",
description: "Tells you your permission level for the current message location.",
usage: "mylevel"
};

27
commands/reboot.js Normal file
View File

@ -0,0 +1,27 @@
const config = require("../config.js");
const { settings } = require("../modules/settings.js");
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
const replying = settings.ensure(message.guild.id, config.defaultSettings).commandReply;
await message.reply({ content: "Bot is shutting down.", allowedMentions: { repliedUser: (replying === "true") }});
await Promise.all(client.container.commands.map(cmd => {
// the path is relative to the *current folder*, so just ./filename.js
delete require.cache[require.resolve(`./${cmd.help.name}.js`)];
// We also need to delete and reload the command from the container.commands Enmap
client.container.commands.delete(cmd.help.name);
}));
process.exit(0);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["restart"],
permLevel: "Bot Admin"
};
exports.help = {
name: "reboot",
category: "System",
description: "Shuts down the bot. If running under PM2, bot will restart automatically.",
usage: "reboot"
};

35
commands/reload.js Normal file
View File

@ -0,0 +1,35 @@
const config = require("../config.js");
const { settings } = require("../modules/settings.js");
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
// Grab the container from the client to reduce line length.
const { container } = client;
const replying = settings.ensure(message.guild.id, config.defaultSettings).commandReply;
if (!args || args.length < 1) return message.reply("Must provide a command name to reload.");
const command = container.commands.get(args[0]) || container.commands.get(container.aliases.get(args[0]));
// Check if the command exists and is valid
if (!command) {
return message.reply("That command does not exist");
}
// the path is relative to the *current folder*, so just ./filename.js
delete require.cache[require.resolve(`./${command.help.name}.js`)];
// We also need to delete and reload the command from the container.commands Enmap
container.commands.delete(command.help.name);
const props = require(`./${command.help.name}.js`);
container.commands.set(command.help.name, props);
message.reply({ content: `The command \`${command.help.name}\` has been reloaded`, allowedMentions: { repliedUser: (replying === "true") }});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Admin"
};
exports.help = {
name: "reload",
category: "System",
description: "Reloads a command that\"s been modified.",
usage: "reload [command]"
};

97
commands/set.js Normal file
View File

@ -0,0 +1,97 @@
// This command is to modify/edit guild configuration. Perm Level 3 for admins
// and owners only. Used for changing prefixes and role names and such.
// Note that there's no "checks" in this basic version - no config "types" like
// Role, String, Int, etc... It's basic, to be extended with your deft hands!
// Note the **destructuring** here. instead of `args` we have :
// [action, key, ...value]
// This gives us the equivalent of either:
// const action = args[0]; const key = args[1]; const value = args.slice(2);
// OR the same as:
// const [action, key, ...value] = args;
const { codeBlock } = require("@discordjs/builders");
const { settings } = require("../modules/settings.js");
const { awaitReply } = require("../modules/functions.js");
exports.run = async (client, message, [action, key, ...value], level) => { // eslint-disable-line no-unused-vars
// Retrieve current guild settings (merged) and overrides only.
const serverSettings = message.settings;
const defaults = settings.get("default");
const overrides = settings.get(message.guild.id);
const replying = serverSettings.commandReply;
if (!settings.has(message.guild.id)) settings.set(message.guild.id, {});
// Edit an existing key value
if (action === "edit") {
// User must specify a key.
if (!key) return message.reply({ content: "Please specify a key to edit", allowedMentions: { repliedUser: (replying === "true") }});
// User must specify a key that actually exists!
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
const joinedValue = value.join(" ");
// User must specify a value to change.
if (joinedValue.length < 1) return message.reply({ content: "Please specify a new value", allowedMentions: { repliedUser: (replying === "true") }});
// User must specify a different value than the current one.
if (joinedValue === serverSettings[key]) return message.reply({ content: "This setting already has that value!", allowedMentions: { repliedUser: (replying === "true") }});
// If the guild does not have any overrides, initialize it.
if (!settings.has(message.guild.id)) settings.set(message.guild.id, {});
// Modify the guild overrides directly.
settings.set(message.guild.id, joinedValue, key);
// Confirm everything is fine!
message.reply({ content: `${key} successfully edited to ${joinedValue}`, allowedMentions: { repliedUser: (replying === "true") }});
} else
// Resets a key to the default value
if (action === "del" || action === "reset") {
if (!key) return message.reply({ content: "Please specify a key to reset.", allowedMentions: { repliedUser: (replying === "true") }});
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
if (!overrides[key]) return message.reply({ content: "This key does not have an override and is already using defaults.", allowedMentions: { repliedUser: (replying === "true") }});
// Good demonstration of the custom awaitReply method in `./modules/functions.js` !
const response = await awaitReply(message, `Are you sure you want to reset ${key} to the default value?`);
// If they respond with y or yes, continue.
if (["y", "yes"].includes(response.toLowerCase())) {
// We delete the `key` here.
settings.delete(message.guild.id, key);
message.reply({ content: `${key} was successfully reset to default.`, allowedMentions: { repliedUser: (replying === "true") }});
} else
// If they respond with n or no, we inform them that the action has been cancelled.
if (["n","no","cancel"].includes(response)) {
message.reply({ content: `Your setting for \`${key}\` remains at \`${serverSettings[key]}\``, allowedMentions: { repliedUser: (replying === "true") }});
}
} else
if (action === "get") {
if (!key) return message.reply({ content: "Please specify a key to view", allowedMentions: { repliedUser: (replying === "true") }});
if (!defaults[key]) return message.reply({ content: "This key does not exist in the settings", allowedMentions: { repliedUser: (replying === "true") }});
const isDefault = !overrides[key] ? "\nThis is the default global default value." : "";
message.reply({ content: `The value of ${key} is currently ${serverSettings[key]}${isDefault}`, allowedMentions: { repliedUser: (replying === "true") }});
} else {
// Otherwise, the default action is to return the whole configuration;
const array = [];
Object.entries(serverSettings).forEach(([key, value]) => {
array.push(`${key}${" ".repeat(20 - key.length)}:: ${value}`);
});
await message.channel.send(codeBlock("asciidoc", `= Current Guild Settings =
${array.join("\n")}`));
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["setting", "settings", "conf"],
permLevel: "Administrator"
};
exports.help = {
name: "set",
category: "System",
description: "View or change settings for your server.",
usage: "set <view/get/edit> <key> <value>"
};

31
commands/stats.js Normal file
View File

@ -0,0 +1,31 @@
const { version } = require("discord.js");
const { codeBlock } = require("@discordjs/builders");
const { DurationFormatter } = require("@sapphire/time-utilities");
const durationFormatter = new DurationFormatter();
exports.run = (client, message, args, level) => { // eslint-disable-line no-unused-vars
const duration = durationFormatter.format(client.uptime);
const stats = codeBlock("asciidoc", `= STATISTICS =
• Mem Usage :: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB
• Uptime :: ${duration}
• Users :: ${client.guilds.cache.map(g => g.memberCount).reduce((a, b) => a + b).toLocaleString()}
• Servers :: ${client.guilds.cache.size.toLocaleString()}
• Channels :: ${client.channels.cache.size.toLocaleString()}
• Discord.js :: v${version}
• Node :: ${process.version}`);
message.channel.send(stats);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "stats",
category: "Miscellaneous",
description: "Gives some useful bot statistics",
usage: "stats"
};