Separated custom logger from command menu

This commit is contained in:
WatDuhHekBro 2021-03-30 03:58:21 -05:00
parent 00addd468c
commit 10c1cd9cff
24 changed files with 6763 additions and 1388 deletions

7794
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
import Command from "../core/command"; import Command from "../core/command";
import {CommonLibrary, logs, botHasPermission, clean} from "../core/lib"; import {CommonLibrary, botHasPermission, clean} from "../core/lib";
import {Config, Storage} from "../core/structures"; import {Config, Storage} from "../core/structures";
import {PermissionNames, getPermissionLevel} from "../core/permissions"; import {PermissionNames, getPermissionLevel} from "../core/permissions";
import {Permissions} from "discord.js"; import {Permissions} from "discord.js";
import * as discord from "discord.js"; import * as discord from "discord.js";
import {logs} from "../globals";
function getLogBuffer(type: string) { function getLogBuffer(type: string) {
return { return {

View File

@ -62,7 +62,7 @@ export function getSendEmbed(sender: User, receiver: User, amount: number): obje
} }
export function isAuthorized(guild: Guild | null, channel: TextChannel | DMChannel | NewsChannel): boolean { export function isAuthorized(guild: Guild | null, channel: TextChannel | DMChannel | NewsChannel): boolean {
if (guild?.id === "637512823676600330" && channel?.id === "669464416420364288" || process.argv[2] === "dev") return true; if ((guild?.id === "637512823676600330" && channel?.id === "669464416420364288") || IS_DEV_MODE) return true;
else { else {
channel.send("Sorry, this command can only be used in Monika's emote server. (#mon-stocks)"); channel.send("Sorry, this command can only be used in Monika's emote server. (#mon-stocks)");
return false; return false;

View File

@ -19,7 +19,9 @@ export default new Command({
const command = commands.get(header); const command = commands.get(header);
if (!command) if (!command)
return $.warn(`Command "${header}" of category "${category}" unexpectedly doesn't exist!`); return console.warn(
`Command "${header}" of category "${category}" unexpectedly doesn't exist!`
);
output += `\n- \`${header}\`: ${command.description}`; output += `\n- \`${header}\`: ${command.description}`;
} }
@ -37,7 +39,7 @@ export default new Command({
if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`!`); if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`!`);
if (command.originalCommandName) header = command.originalCommandName; if (command.originalCommandName) header = command.originalCommandName;
else $.warn(`originalCommandName isn't defined for ${header}?!`); else console.warn(`originalCommandName isn't defined for ${header}?!`);
let permLevel = command.permission ?? Command.PERMISSIONS.NONE; let permLevel = command.permission ?? Command.PERMISSIONS.NONE;
let usage = command.usage; let usage = command.usage;
@ -48,7 +50,7 @@ export default new Command({
for (const [category, headers] of categories) { for (const [category, headers] of categories) {
if (headers.includes(header)) { if (headers.includes(header)) {
if (selectedCategory !== "Unknown") if (selectedCategory !== "Unknown")
$.warn( console.warn(
`Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.`
); );
else selectedCategory = $(category).toTitleCase(); else selectedCategory = $(category).toTitleCase();

View File

@ -131,7 +131,7 @@ export default new Command({
continueReactionLoop = false; continueReactionLoop = false;
if (reaction.count !== userReactions + botReactions) { if (reaction.count !== userReactions + botReactions) {
$.warn( console.warn(
`[Channel: ${channel.id}, Message: ${msg.id}] A reaction count of ${reaction.count} was expected but was given ${userReactions} user reactions and ${botReactions} bot reactions.` `[Channel: ${channel.id}, Message: ${msg.id}] A reaction count of ${reaction.count} was expected but was given ${userReactions} user reactions and ${botReactions} bot reactions.`
); );
warnings++; warnings++;
@ -161,7 +161,7 @@ export default new Command({
"y" "y"
)}.` )}.`
); );
$.log(`Finished operation in ${finishTime - startTime} ms.`); console.log(`Finished operation in ${finishTime - startTime} ms.`);
$.channel.stopTyping(); $.channel.stopTyping();
// Display stats on emote usage. // Display stats on emote usage.

View File

@ -1,4 +1,4 @@
import $, {isType, parseVars, CommonLibrary} from "./lib"; import {isType, parseVars, CommonLibrary} from "./lib";
import {Collection} from "discord.js"; import {Collection} from "discord.js";
import {PERMISSIONS} from "./permissions"; import {PERMISSIONS} from "./permissions";
import {getPrefix} from "../core/structures"; import {getPrefix} from "../core/structures";
@ -68,11 +68,11 @@ export default class Command {
for (const alias of aliases) { for (const alias of aliases) {
if (baseSubcommands.includes(alias)) if (baseSubcommands.includes(alias))
$.warn( console.warn(
`"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)`
); );
else if (this.subcommands.has(alias)) else if (this.subcommands.has(alias))
$.warn( console.warn(
`Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)`
); );
else this.subcommands.set(alias, subcmd); else this.subcommands.set(alias, subcmd);
@ -81,17 +81,17 @@ export default class Command {
} }
if (this.user && this.user.aliases.length > 0) if (this.user && this.user.aliases.length > 0)
$.warn( console.warn(
`There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` `There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
); );
if (this.number && this.number.aliases.length > 0) if (this.number && this.number.aliases.length > 0)
$.warn( console.warn(
`There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` `There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
); );
if (this.any && this.any.aliases.length > 0) if (this.any && this.any.aliases.length > 0)
$.warn( console.warn(
`There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` `There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
); );
} }
@ -179,7 +179,7 @@ export const loadableCommands = (async () => {
command.originalCommandName = commandName; command.originalCommandName = commandName;
if (commands.has(commandName)) { if (commands.has(commandName)) {
$.warn( console.warn(
`Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!` `Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!`
); );
} else { } else {
@ -188,7 +188,7 @@ export const loadableCommands = (async () => {
for (const alias of command.aliases) { for (const alias of command.aliases) {
if (commands.has(alias)) { if (commands.has(alias)) {
$.warn( console.warn(
`Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!` `Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!`
); );
} else { } else {
@ -199,9 +199,9 @@ export const loadableCommands = (async () => {
if (!(category in lists)) lists[category] = []; if (!(category in lists)) lists[category] = [];
lists[category].push(commandName); lists[category].push(commandName);
$.log(`Loading Command: ${commandID}`); console.log(`Loading Command: ${commandID}`);
} else { } else {
$.warn(`Command "${commandID}" has no default export which is a Command instance!`); console.warn(`Command "${commandID}" has no default export which is a Command instance!`);
} }
} }
} }

View File

@ -1,6 +1,5 @@
import {Client, ClientEvents, Constants} from "discord.js"; import {Client, ClientEvents, Constants} from "discord.js";
import Storage from "./storage"; import Storage from "./storage";
import $ from "./lib";
interface EventOptions<K extends keyof ClientEvents> { interface EventOptions<K extends keyof ClientEvents> {
readonly on?: (...args: ClientEvents[K]) => void; readonly on?: (...args: ClientEvents[K]) => void;
@ -30,9 +29,9 @@ export async function loadEvents(client: Client) {
if ((Object.values(Constants.Events) as string[]).includes(header)) { if ((Object.values(Constants.Events) as string[]).includes(header)) {
event.attach(client, header); event.attach(client, header);
$.log(`Loading Event: ${header}`); console.log(`Loading Event: ${header}`);
} else } else
$.warn( console.warn(
`"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)` `"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)`
); );
} }

View File

@ -1,6 +1,5 @@
import {GenericWrapper, NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers"; import {GenericWrapper, NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers";
import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, Permissions} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, Permissions} from "discord.js";
import chalk from "chalk";
import {get} from "https"; import {get} from "https";
import FileManager from "./storage"; import FileManager from "./storage";
import {eventListeners} from "../events/messageReactionRemove"; import {eventListeners} from "../events/messageReactionRemove";
@ -19,11 +18,6 @@ export interface CommonLibrary {
// Common Library Functions // // Common Library Functions //
/** <Promise>.catch($.handler.bind($)) or <Promise>.catch(error => $.handler(error)) */ /** <Promise>.catch($.handler.bind($)) or <Promise>.catch(error => $.handler(error)) */
handler: (error: Error) => void; handler: (error: Error) => void;
log: (...args: any[]) => void;
warn: (...args: any[]) => void;
error: (...args: any[]) => void;
debug: (...args: any[]) => void;
ready: (...args: any[]) => void;
paginate: ( paginate: (
message: Message, message: Message,
senderID: string, senderID: string,
@ -79,94 +73,13 @@ $.handler = function (this: CommonLibrary, error: Error) {
`There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\`` `There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\``
); );
else else
$.warn( console.warn(
"No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!"
); );
$.error(error); console.error(error);
}; };
// Logs with different levels of verbosity.
export const logs: {[type: string]: string} = {
error: "",
warn: "",
info: "",
verbose: ""
};
let enabled = true;
export function setConsoleActivated(activated: boolean) {
enabled = activated;
}
// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log.
// General Purpose Logger
$.log = (...args: any[]) => {
if (enabled) console.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgWhite("INFO"), ...args);
const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(" ")}\n`;
logs.info += text;
logs.verbose += text;
};
// "It'll still work, but you should really check up on this."
$.warn = (...args: any[]) => {
if (enabled) console.warn(chalk.white.bgGray(formatTimestamp()), chalk.black.bgYellow("WARN"), ...args);
const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(" ")}\n`;
logs.warn += text;
logs.info += text;
logs.verbose += text;
};
// Used for anything which prevents the program from actually running.
$.error = (...args: any[]) => {
if (enabled) console.error(chalk.white.bgGray(formatTimestamp()), chalk.white.bgRed("ERROR"), ...args);
const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(" ")}\n`;
logs.error += text;
logs.warn += text;
logs.info += text;
logs.verbose += text;
};
// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose".
// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> <path>/::(<object>.)<function>(<args>) = <value>
// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests.
$.debug = (...args: any[]) => {
if (process.argv[2] === "dev" && enabled)
console.debug(chalk.white.bgGray(formatTimestamp()), chalk.white.bgBlue("DEBUG"), ...args);
const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(" ")}\n`;
logs.verbose += text;
};
// Used once at the start of the program when the bot loads.
$.ready = (...args: any[]) => {
if (enabled) console.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgGreen("READY"), ...args);
const text = `[${formatUTCTimestamp()}] [READY] ${args.join(" ")}\n`;
logs.info += text;
logs.verbose += text;
};
export function formatTimestamp(now = new Date()) {
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, "0");
const day = now.getDate().toString().padStart(2, "0");
const hour = now.getHours().toString().padStart(2, "0");
const minute = now.getMinutes().toString().padStart(2, "0");
const second = now.getSeconds().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
export function formatUTCTimestamp(now = new Date()) {
const year = now.getUTCFullYear();
const month = (now.getUTCMonth() + 1).toString().padStart(2, "0");
const day = now.getUTCDate().toString().padStart(2, "0");
const hour = now.getUTCHours().toString().padStart(2, "0");
const minute = now.getUTCMinutes().toString().padStart(2, "0");
const second = now.getUTCSeconds().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
export function botHasPermission(guild: Guild | null, permission: number): boolean { export function botHasPermission(guild: Guild | null, permission: number): boolean {
return !!guild?.me?.hasPermission(permission); return !!guild?.me?.hasPermission(permission);
} }

View File

@ -1,6 +1,5 @@
import {GuildMember, Permissions} from "discord.js"; import {GuildMember, Permissions} from "discord.js";
import {Config} from "./structures"; import {Config} from "./structures";
import $ from "./lib";
export enum PERMISSIONS { export enum PERMISSIONS {
NONE, NONE,
@ -70,7 +69,7 @@ export function getPermissionLevel(member: GuildMember): number {
// By transitive property, lenNames and lenChecker have to be equal to each other as well. // By transitive property, lenNames and lenChecker have to be equal to each other as well.
if (length !== lenNames || length !== lenChecker) if (length !== lenNames || length !== lenChecker)
$.error( console.error(
`Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!` `Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!`
); );
})(); })();

View File

@ -1,5 +1,4 @@
import fs from "fs"; import fs from "fs";
import $ from "./lib";
const Storage = { const Storage = {
read(header: string): object { read(header: string): object {
@ -14,7 +13,7 @@ const Storage = {
data = JSON.parse(file); data = JSON.parse(file);
} catch (error) { } catch (error) {
if (process.argv[2] !== "dev") { if (process.argv[2] !== "dev") {
$.warn(`Malformed JSON data (header: ${header}), backing it up.`, file); console.warn(`Malformed JSON data (header: ${header}), backing it up.`, file);
fs.writeFile( fs.writeFile(
`${path}.backup`, `${path}.backup`,
file, file,
@ -30,7 +29,7 @@ const Storage = {
this.open("data"); this.open("data");
const path = `data/${header}.json`; const path = `data/${header}.json`;
if (process.argv[2] === "dev" || header === "config") { if (IS_DEV_MODE || header === "config") {
const result = JSON.stringify(data, null, "\t"); const result = JSON.stringify(data, null, "\t");
if (asynchronous) if (asynchronous)
@ -60,8 +59,8 @@ const Storage = {
export function generateHandler(message: string) { export function generateHandler(message: string) {
return (error: Error | null) => { return (error: Error | null) => {
if (error) $.error(error); if (error) console.error(error);
else $.debug(message); else console.debug(message);
}; };
} }

View File

@ -1,5 +1,5 @@
import FileManager from "./storage"; import FileManager from "./storage";
import $, {select, GenericJSON, GenericStructure} from "./lib"; import {select, GenericJSON, GenericStructure} from "./lib";
import {watch} from "fs"; import {watch} from "fs";
import {Guild as DiscordGuild, Snowflake} from "discord.js"; import {Guild as DiscordGuild, Snowflake} from "discord.js";
@ -63,7 +63,7 @@ class StorageStructure extends GenericStructure {
/** Gets a user's profile if they exist and generate one if not. */ /** Gets a user's profile if they exist and generate one if not. */
public getUser(id: string): User { public getUser(id: string): User {
if (!/\d{17,19}/g.test(id)) if (!/\d{17,19}/g.test(id))
$.warn(`"${id}" is not a valid user ID! It will be erased when the data loads again.`); console.warn(`"${id}" is not a valid user ID! It will be erased when the data loads again.`);
if (id in this.users) return this.users[id]; if (id in this.users) return this.users[id];
else { else {
@ -76,7 +76,7 @@ class StorageStructure extends GenericStructure {
/** Gets a guild's settings if they exist and generate one if not. */ /** Gets a guild's settings if they exist and generate one if not. */
public getGuild(id: string): Guild { public getGuild(id: string): Guild {
if (!/\d{17,19}/g.test(id)) if (!/\d{17,19}/g.test(id))
$.warn(`"${id}" is not a valid guild ID! It will be erased when the data loads again.`); console.warn(`"${id}" is not a valid guild ID! It will be erased when the data loads again.`);
if (id in this.guilds) return this.guilds[id]; if (id in this.guilds) return this.guilds[id];
else { else {
@ -93,9 +93,9 @@ export let Storage = new StorageStructure(FileManager.read("storage"));
// This part will allow the user to manually edit any JSON files they want while the program is running which'll update the program's cache. // This part will allow the user to manually edit any JSON files they want while the program is running which'll update the program's cache.
// However, fs.watch is a buggy mess that should be avoided in production. While it helps test out stuff for development, it's not a good idea to have it running outside of development as it causes all sorts of issues. // However, fs.watch is a buggy mess that should be avoided in production. While it helps test out stuff for development, it's not a good idea to have it running outside of development as it causes all sorts of issues.
if (process.argv[2] === "dev") { if (IS_DEV_MODE) {
watch("data", (event, filename) => { watch("data", (event, filename) => {
$.debug("File Watcher:", event, filename); console.debug("File Watcher:", event, filename);
const header = filename.substring(0, filename.indexOf(".json")); const header = filename.substring(0, filename.indexOf(".json"));
switch (header) { switch (header) {

View File

@ -1,6 +1,5 @@
import Event from "../core/event"; import Event from "../core/event";
import {client} from "../index"; import {client} from "../index";
import $ from "../core/lib";
import * as discord from "discord.js"; import * as discord from "discord.js";
export default new Event<"channelCreate">({ export default new Event<"channelCreate">({
@ -8,7 +7,7 @@ export default new Event<"channelCreate">({
const botGuilds = client.guilds; const botGuilds = client.guilds;
if (channel instanceof discord.GuildChannel) { if (channel instanceof discord.GuildChannel) {
const createdGuild = await botGuilds.fetch(channel.guild.id); const createdGuild = await botGuilds.fetch(channel.guild.id);
$.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`);
} }
} }
}); });

View File

@ -1,6 +1,5 @@
import Event from "../core/event"; import Event from "../core/event";
import {client} from "../index"; import {client} from "../index";
import $ from "../core/lib";
import * as discord from "discord.js"; import * as discord from "discord.js";
export default new Event<"channelDelete">({ export default new Event<"channelDelete">({
@ -8,7 +7,7 @@ export default new Event<"channelDelete">({
const botGuilds = client.guilds; const botGuilds = client.guilds;
if (channel instanceof discord.GuildChannel) { if (channel instanceof discord.GuildChannel) {
const createdGuild = await botGuilds.fetch(channel.guild.id); const createdGuild = await botGuilds.fetch(channel.guild.id);
$.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`);
} }
} }
}); });

View File

@ -1,10 +1,9 @@
import Event from "../core/event"; import Event from "../core/event";
import $ from "../core/lib";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"emojiCreate">({ export default new Event<"emojiCreate">({
on(emote) { on(emote) {
$.log(`Updated emote registry. ${emote.name}`); console.log(`Updated emote registry. ${emote.name}`);
updateGlobalEmoteRegistry(); updateGlobalEmoteRegistry();
} }
}); });

View File

@ -1,10 +1,9 @@
import Event from "../core/event"; import Event from "../core/event";
import $ from "../core/lib";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"emojiDelete">({ export default new Event<"emojiDelete">({
on() { on() {
$.log("Updated emote registry."); console.log("Updated emote registry.");
updateGlobalEmoteRegistry(); updateGlobalEmoteRegistry();
} }
}); });

View File

@ -1,10 +1,9 @@
import Event from "../core/event"; import Event from "../core/event";
import $ from "../core/lib";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"emojiUpdate">({ export default new Event<"emojiUpdate">({
on() { on() {
$.log("Updated emote registry."); console.log("Updated emote registry.");
updateGlobalEmoteRegistry(); updateGlobalEmoteRegistry();
} }
}); });

View File

@ -1,10 +1,9 @@
import Event from "../core/event"; import Event from "../core/event";
import $ from "../core/lib";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"guildCreate">({ export default new Event<"guildCreate">({
on() { on() {
$.log("Updated emote registry."); console.log("Updated emote registry.");
updateGlobalEmoteRegistry(); updateGlobalEmoteRegistry();
} }
}); });

View File

@ -1,10 +1,9 @@
import Event from "../core/event"; import Event from "../core/event";
import $ from "../core/lib";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"guildDelete">({ export default new Event<"guildDelete">({
on() { on() {
$.log("Updated emote registry."); console.log("Updated emote registry.");
updateGlobalEmoteRegistry(); updateGlobalEmoteRegistry();
} }
}); });

View File

@ -82,13 +82,13 @@ export default new Event<"message">({
); );
} }
$.log( console.log(
`${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".`
); );
// Subcommand Recursion // // Subcommand Recursion //
let command = commands.get(header); let command = commands.get(header);
if (!command) return $.warn(`Command "${header}" was called but for some reason it's still undefined!`); if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`);
const params: any[] = []; const params: any[] = [];
let isEndpoint = false; let isEndpoint = false;
let permLevel = command.permission ?? Command.PERMISSIONS.NONE; let permLevel = command.permission ?? Command.PERMISSIONS.NONE;
@ -96,7 +96,7 @@ export default new Event<"message">({
for (let param of args) { for (let param of args) {
if (command.endpoint) { if (command.endpoint) {
if (command.subcommands.size > 0 || command.user || command.number || command.any) if (command.subcommands.size > 0 || command.user || command.number || command.any)
$.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`);
isEndpoint = true; isEndpoint = true;
break; break;
} }
@ -117,7 +117,7 @@ export default new Event<"message">({
} }
if (!message.member) if (!message.member)
return $.warn("This command was likely called from a DM channel meaning the member object is null."); return console.warn("This command was likely called from a DM channel meaning the member object is null.");
if (!hasPermission(message.member, permLevel)) { if (!hasPermission(message.member, permLevel)) {
const userPermLevel = getPermissionLevel(message.member); const userPermLevel = getPermissionLevel(message.member);

View File

@ -1,13 +1,12 @@
import Event from "../core/event"; import Event from "../core/event";
import {client} from "../index"; import {client} from "../index";
import $ from "../core/lib";
import {Config} from "../core/structures"; import {Config} from "../core/structures";
import {updateGlobalEmoteRegistry} from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib";
export default new Event<"ready">({ export default new Event<"ready">({
once() { once() {
if (client.user) { if (client.user) {
$.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`);
client.user.setActivity({ client.user.setActivity({
type: "LISTENING", type: "LISTENING",
name: `${Config.prefix}help` name: `${Config.prefix}help`

86
src/globals.ts Normal file
View File

@ -0,0 +1,86 @@
import chalk from "chalk";
declare global {
var IS_DEV_MODE: boolean;
interface Console {
ready: (...data: any[]) => void;
}
}
global.IS_DEV_MODE = process.argv[2] === "dev";
const oldConsole = console;
export const logs: {[type: string]: string} = {
error: "",
warn: "",
info: "",
verbose: ""
};
function formatTimestamp(now = new Date()) {
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, "0");
const day = now.getDate().toString().padStart(2, "0");
const hour = now.getHours().toString().padStart(2, "0");
const minute = now.getMinutes().toString().padStart(2, "0");
const second = now.getSeconds().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
function formatUTCTimestamp(now = new Date()) {
const year = now.getUTCFullYear();
const month = (now.getUTCMonth() + 1).toString().padStart(2, "0");
const day = now.getUTCDate().toString().padStart(2, "0");
const hour = now.getUTCHours().toString().padStart(2, "0");
const minute = now.getUTCMinutes().toString().padStart(2, "0");
const second = now.getUTCSeconds().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log.
console = {
...oldConsole,
// General Purpose Logger
log(...args: any[]) {
oldConsole.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgWhite("INFO"), ...args);
const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(" ")}\n`;
logs.info += text;
logs.verbose += text;
},
// "It'll still work, but you should really check up on this."
warn(...args: any[]) {
oldConsole.warn(chalk.white.bgGray(formatTimestamp()), chalk.black.bgYellow("WARN"), ...args);
const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(" ")}\n`;
logs.warn += text;
logs.info += text;
logs.verbose += text;
},
// Used for anything which prevents the program from actually running.
error(...args: any[]) {
oldConsole.error(chalk.white.bgGray(formatTimestamp()), chalk.white.bgRed("ERROR"), ...args);
const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(" ")}\n`;
logs.error += text;
logs.warn += text;
logs.info += text;
logs.verbose += text;
},
// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose".
// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> <path>/::(<object>.)<function>(<args>) = <value>
// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests.
debug(...args: any[]) {
if (IS_DEV_MODE) oldConsole.debug(chalk.white.bgGray(formatTimestamp()), chalk.white.bgBlue("DEBUG"), ...args);
const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(" ")}\n`;
logs.verbose += text;
},
// Used once at the start of the program when the bot loads.
ready(...args: any[]) {
oldConsole.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgGreen("READY"), ...args);
const text = `[${formatUTCTimestamp()}] [READY] ${args.join(" ")}\n`;
logs.info += text;
logs.verbose += text;
}
};
console.log("Loading globals...");

View File

@ -1,3 +1,4 @@
import "./globals";
import * as discord from "discord.js"; import * as discord from "discord.js";
import setup from "./setup"; import setup from "./setup";
import {Config} from "./core/structures"; import {Config} from "./core/structures";

View File

@ -1,60 +1,61 @@
import { client } from '..' import {client} from "..";
import { Message, TextChannel, APIMessage, MessageEmbed } from 'discord.js' import {Message, TextChannel, APIMessage, MessageEmbed} from "discord.js";
import { getPrefix } from '../core/structures' import {getPrefix} from "../core/structures";
import { DiscordAPIError } from 'discord.js' import {DiscordAPIError} from "discord.js";
export default async function quote(message: Message) { export default async function quote(message: Message) {
if (message.author.bot) return if (message.author.bot) return;
// const message_link_regex = message.content.match(/(!)?https?:\/\/\w+\.com\/channels\/(\d+)\/(\d+)\/(\d+)/) // const message_link_regex = message.content.match(/(!)?https?:\/\/\w+\.com\/channels\/(\d+)\/(\d+)\/(\d+)/)
const message_link_regex = message.content.match(/([<!]?)https?:\/\/(?:ptb\.|canary\.|)discord(?:app)?\.com\/channels\/(\d+)\/(\d+)\/(\d+)(>?)/) const message_link_regex = message.content.match(
/([<!]?)https?:\/\/(?:ptb\.|canary\.|)discord(?:app)?\.com\/channels\/(\d+)\/(\d+)\/(\d+)(>?)/
);
if (message_link_regex == null) return if (message_link_regex == null) return;
const [, char, guildID, channelID, messageID] = message_link_regex const [, char, guildID, channelID, messageID] = message_link_regex;
if (char || message.content.startsWith(getPrefix(message.guild))) return if (char || message.content.startsWith(getPrefix(message.guild))) return;
try { try {
const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel;
const link_message = await channel.messages.fetch(messageID) const link_message = await channel.messages.fetch(messageID);
let rtmsg: string | APIMessage = '' let rtmsg: string | APIMessage = "";
if (link_message.cleanContent) { if (link_message.cleanContent) {
rtmsg = new APIMessage(message.channel as TextChannel, { rtmsg = new APIMessage(message.channel as TextChannel, {
content: link_message.cleanContent, content: link_message.cleanContent,
disableMentions: 'all', disableMentions: "all",
files: link_message.attachments.array() files: link_message.attachments.array()
}) });
} }
const embeds = [ const embeds = [...link_message.embeds.filter((v) => v.type == "rich"), ...link_message.attachments.values()];
...link_message.embeds.filter(v => v.type == 'rich'),
...link_message.attachments.values()
]
/// @ts-ignore /// @ts-ignore
if (!link_message.cleanContent && embeds.empty) { if (!link_message.cleanContent && embeds.empty) {
const Embed = new MessageEmbed() const Embed = new MessageEmbed().setDescription("🚫 The message is empty.");
.setDescription('🚫 The message is empty.') return message.channel.send(Embed);
return message.channel.send(Embed)
} }
const infoEmbed = new MessageEmbed() const infoEmbed = new MessageEmbed()
.setAuthor( .setAuthor(
link_message.author.username, link_message.author.username,
link_message.author.displayAvatarURL({format: 'png', dynamic: true, size: 4096})) link_message.author.displayAvatarURL({format: "png", dynamic: true, size: 4096})
)
.setTimestamp(link_message.createdTimestamp) .setTimestamp(link_message.createdTimestamp)
.setDescription(`${link_message.cleanContent}\n\nSent in **${link_message.guild?.name}** | <#${link_message.channel.id}> ([link](https://discord.com/channels/${guildID}/${channelID}/${messageID}))`); .setDescription(
if (link_message.attachments.size !== 0) { `${link_message.cleanContent}\n\nSent in **${link_message.guild?.name}** | <#${link_message.channel.id}> ([link](https://discord.com/channels/${guildID}/${channelID}/${messageID}))`
const image = link_message.attachments.first(); );
/// @ts-ignore if (link_message.attachments.size !== 0) {
infoEmbed.setImage(image.url); const image = link_message.attachments.first();
} /// @ts-ignore
infoEmbed.setImage(image.url);
}
await message.channel.send(infoEmbed) await message.channel.send(infoEmbed);
} catch (error) { } catch (error) {
if (error instanceof DiscordAPIError) { if (error instanceof DiscordAPIError) {
message.channel.send("I don't have access to this channel, or something else went wrong.") message.channel.send("I don't have access to this channel, or something else went wrong.");
} }
return console.error(error) return console.error(error);
} }
} }

View File

@ -2,12 +2,11 @@ import {existsSync as exists, readFileSync as read, writeFile as write} from "fs
import inquirer from "inquirer"; import inquirer from "inquirer";
import Storage, {generateHandler} from "./core/storage"; import Storage, {generateHandler} from "./core/storage";
import {Config} from "./core/structures"; import {Config} from "./core/structures";
import $, {setConsoleActivated} from "./core/lib";
// The template should be built with a reductionist mentality. // The template should be built with a reductionist mentality.
// Provide everything the user needs and then let them remove whatever they want. // Provide everything the user needs and then let them remove whatever they want.
// That way, they aren't focusing on what's missing, but rather what they need for their command. // That way, they aren't focusing on what's missing, but rather what they need for their command.
if (process.argv[2] === "dev" && !exists("src/commands/test.ts")) { if (IS_DEV_MODE && !exists("src/commands/test.ts")) {
write( write(
"src/commands/test.ts", "src/commands/test.ts",
read("src/commands/template.ts"), read("src/commands/template.ts"),
@ -64,8 +63,19 @@ export default {
}, },
/** Prompt the user to set their token again. */ /** Prompt the user to set their token again. */
async again() { async again() {
$.error("It seems that the token you provided is invalid."); console.error("It seems that the token you provided is invalid.");
setConsoleActivated(false);
// Deactivate the console //
const oldConsole = console;
console = {
...oldConsole,
log() {},
warn() {},
error() {},
debug() {},
ready() {}
};
const answers = await inquirer.prompt(prompts.slice(0, 1)); const answers = await inquirer.prompt(prompts.slice(0, 1));
Config.token = answers.token as string; Config.token = answers.token as string;
Config.save(false); Config.save(false);