From d2886ad82bcafc7c5658b3b3744e0e6720fb6be5 Mon Sep 17 00:00:00 2001 From: Maseshi Date: Wed, 26 Apr 2023 14:38:30 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=EF=B8=8F=20UPDATE:=20Optimize=20co?= =?UTF-8?q?de=20and=20add=20new=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/utils/clientUtils.js | 165 ++++++++++---------- source/utils/consoleUtils.js | 102 +++++++------ source/utils/databaseUtils.js | 273 ++++++++++++++++++---------------- source/utils/miscUtils.js | 28 +++- 4 files changed, 315 insertions(+), 253 deletions(-) diff --git a/source/utils/clientUtils.js b/source/utils/clientUtils.js index 7d6d4f09..0b1b2f08 100644 --- a/source/utils/clientUtils.js +++ b/source/utils/clientUtils.js @@ -1,20 +1,32 @@ const { REST, Routes } = require("discord.js"); const { ansiColor } = require("./consoleUtils"); -const fetch = require("node-fetch"); +const { get } = require("axios").default; const packages = require("../../package.json"); +/** + * Check for updates via Github and notify when new updates are available. + * + * @param {Client} client + */ const checkForUpdates = async (client) => { const clearStyle = ansiColor(0, "sgr"); const greenColor = ansiColor(10, "foreground"); const blueBrightColor = ansiColor(33, "foreground"); + if (!client.config.check_update.enable) return; + if (!client.config.check_update.releases_url) { + return client.console.add("check-update-loading", { + "text": "The releases_url value was not found in the environment. Cancel check for updates.", + "status": "non-spinnable" + }); + } + client.console.add("check-update-loading", { "text": "Checking for new version" }); try { - const response = await fetch("https://api.github.com/repos/Maseshi/Shioru/releases/latest"); - const data = await response.json(); + const response = await get(client.config.check_update.releases_url); if (response.status !== 200) { return client.console.update("check-update-loading", { @@ -22,37 +34,40 @@ const checkForUpdates = async (client) => { "status": "non-spinnable" }); } - if (data) { - if (packages.version.replace(/[^0-9]/g, "") >= data.tag_name.replace(/[^0-9]/g, "")) { + if (response.data) { + if (packages.version >= response.data.tag_name) { client.console.update("check-update-loading", { "text": "Currently using the latest version.", "status": "non-spinnable" }); } else { client.console.update("check-update-loading", { - "text": ".\n" + - ". Update is available " + packages.version + " -> " + greenColor + data.tag_name + clearStyle + "\n" + - ". Run " + blueBrightColor + "npm pull" + clearStyle + " to update\n" + + "text": [ ".", + ". Update is available " + packages.version + " -> " + greenColor + response.data.tag_name + clearStyle, + ". Run " + blueBrightColor + "npm pull" + clearStyle + " to update", + "." + ].join("\n"), "status": "non-spinnable" }); } } } catch (error) { client.console.fail("check-update-loading", { - "failColor": "redBright", - "text": "Failed to check for new updates" + "text": "Failed to check for new updates\n" + error }); - console.group(); - console.error(error); - console.groupEnd(); } } +/** + * Update information of all application commands both **global** and **guild**. + * + * @param {Client} client + * @param {Boolean} reload If set to `true`, no messages will be displayed on the console. + */ const updateApplicationCommands = async (client, reload = false) => { - const commands = client.commands; const token = client.config.token; - const guildID = client.config.testGuild; + const guildID = client.config.test.guild; const clientID = client.user.id; const rest = new REST({ "version": "10" }).setToken(token); @@ -63,29 +78,24 @@ const updateApplicationCommands = async (client, reload = false) => { } try { - const command = await commands.map((commands) => commands.function.command.data); - const context = await commands.map((commands) => { - if (commands.function.context) return commands.function.context.data; - }).filter((element) => element !== undefined) + const command = await client.commands.map((commands) => commands.function.command.data); + const context = await client.contexts.map((commands) => commands.function.context.data); const data = command.concat(context); - if (client.mode === "start") { + if (client.mode === "start" || !client.config.test.application_commands) { await rest.put( Routes.applicationCommands(clientID), { "body": data } ); } else { - if (guildID) { - await rest.put( - Routes.applicationGuildCommands(clientID, guildID), - { "body": data } - ); - } else { - client.console.fail("app-commands-loading", { - "text": "The testGuild key was not found in the environment. You may not be able to see recent commands.", - "status": "non-spinnable" - }); - } + if (!guildID) return client.console.fail("app-commands-loading", { + "text": "The test_guild key was not found in the environment. You may not be able to see recent commands." + }); + + await rest.put( + Routes.applicationGuildCommands(clientID, guildID), + { "body": data } + ); } if (!reload) { client.console.update("app-commands-loading", { @@ -95,60 +105,57 @@ const updateApplicationCommands = async (client, reload = false) => { } } catch (error) { if (!reload) { - client.console.update("app-commands-loading", { - "text": "Application commands could not be completely reloaded.", - "status": "non-spinnable" + client.console.fail("app-commands-loading", { + "text": "Application commands could not be completely reloaded.\n" + error }); } - - console.group(); - console.error(error); - console.groupEnd(); } } const BitwisePermissionFlags = { - 0x1: "Create Invite", - 0x2: "Kick Members", - 0x4: "Ban Members", - 0x8: "Administrator", - 0x10: "Manage Channels", - 0x20: "Manage Server", - 0x40: "Add Reactions", - 0x80: "View Audit Log", - 0x100: "Priority Speaker", - 0x200: "Video", - 0x400: "Read Text Channels & See Voice Channels", - 0x800: "Send Messages", - 0x1000: "Send TTS Messages", - 0x2000: "Manage Messages", - 0x4000: "Embed Links", - 0x8000: "Attach Files", - 0x10000: "Read Message History", - 0x20000: "Mention @everyone, @here, and All Roles", - 0x40000: "Use External Emojis", - 0x80000: "View Server Insights", - 0x100000: "Connect", - 0x200000: "Speak", - 0x400000: "Mute Members", - 0x800000: "Deafen Members", - 0x1000000: "Move Members", - 0x2000000: "Use Voice Activity", - 0x4000000: "Change Nickname", - 0x8000000: "Manage Nicknames", - 0x10000000: "Manage Roles", - 0x20000000: "Manage Webhooks", - 0x40000000: "Manage Emojis & Stickers", - 0x80000000: "Use Application Commands", - 0x100000000: "Request to Speak", - 0x200000000: "Manage Events", - 0x400000000: "Manage Threads", - 0x800000000: "Create Public Threads", - 0x1000000000: "Create Private Threads", - 0x2000000000: "Use External Stickers", - 0x4000000000: "Send Messages in Threads", - 0x8000000000: "Use Embedded Activities", - 0x10000000000: "Moderate Members" + 0x0000000000000001: "Create Invite", + 0x0000000000000002: "Kick Members", + 0x0000000000000004: "Ban Members", + 0x0000000000000008: "Administrator", + 0x0000000000000010: "Manage Channels", + 0x0000000000000020: "Manage Server", + 0x0000000000000040: "Add Reactions", + 0x0000000000000080: "View Audit Log", + 0x0000000000000100: "Priority Speaker", + 0x0000000000000200: "Video", + 0x0000000000000400: "Read Text Channels & See Voice Channels", + 0x0000000000000800: "Send Messages", + 0x0000000000001000: "Send TTS Messages", + 0x0000000000002000: "Manage Messages", + 0x0000000000004000: "Embed Links", + 0x0000000000008000: "Attach Files", + 0x0000000000010000: "Read Message History", + 0x0000000000020000: "Mention @everyone, @here, and All Roles", + 0x0000000000040000: "Use External Emojis", + 0x0000000000080000: "View Server Insights", + 0x0000000000100000: "Connect", + 0x0000000000200000: "Speak", + 0x0000000000400000: "Mute Members", + 0x0000000000800000: "Deafen Members", + 0x0000000001000000: "Move Members", + 0x0000000002000000: "Use Voice Activity", + 0x0000000004000000: "Change Nickname", + 0x0000000008000000: "Manage Nicknames", + 0x0000000010000000: "Manage Roles", + 0x0000000020000000: "Manage Webhooks", + 0x0000000040000000: "Manage Emojis & Stickers", + 0x0000000080000000: "Use Application Commands", + 0x0000000100000000: "Request to Speak", + 0x0000000200000000: "Manage Events", + 0x0000000400000000: "Manage Threads", + 0x0000000800000000: "Create Public Threads", + 0x0000001000000000: "Create Private Threads", + 0x0000002000000000: "Use External Stickers", + 0x0000004000000000: "Send Messages in Threads", + 0x0000008000000000: "Use Embedded Activities", + 0x0000010000000000: "Moderate Members", + 0x0000020000000000: "View Creator Monetization Analytics", + 0x0000040000000000: "Use Soundboard" } module.exports = { diff --git a/source/utils/consoleUtils.js b/source/utils/consoleUtils.js index f53ca314..33e87c88 100644 --- a/source/utils/consoleUtils.js +++ b/source/utils/consoleUtils.js @@ -1,22 +1,16 @@ -const { format } = require("util"); +const { format } = require("node:util"); const { getApps } = require("firebase/app"); const { createWriteStream, existsSync, mkdirSync } = require("node:fs"); -const discord = require("discord.js"); const packages = require("../../package.json"); -const asciiArt = "███████╗██╗ ██╗██╗ ██████╗ ██████╗ ██╗ ██╗ %s1\n" + - "██╔════╝██║ ██║██║██╔═══██╗██╔══██╗██║ ██║ %s2\n" + - "███████╗███████║██║██║ ██║██████╔╝██║ ██║ %s3\n" + - "╚════██║██╔══██║██║██║ ██║██╔══██╗██║ ██║ %s4\n" + - "███████║██║ ██║██║╚██████╔╝██║ ██║╚██████╔╝ %s5\n" + - "╚══════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ %s6" - +/** + * 8-bit: 256-color mode\ + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * + * @param {Number} code Color codes in ANSI + * @param {String} mode Supports 3 modes: **foreground**, **background** and **sgr**. + */ const ansiColor = (code, mode) => { - // 8-bit: 256-color mode - // foreground: ESC[38;5;#m - // background: ESC[48;5;#m - // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit - if (code === null) return console.log("[ansiColor] Please configure the color of ANSI."); if (!mode) return console.log("[ansiColor] Please confirm the ANSI mode, including 'foreground', 'background' and 'sgr'."); @@ -25,7 +19,17 @@ const ansiColor = (code, mode) => { if (mode === "sgr") return "\x1b[" + code.toString() + "m"; } -const catchError = async (client, message, name, error) => { +/** + * Detects errors and informs the user about them. + * + * @param {Client} client + * @param {String} message + * @param {String} name The name of the command or event. + * @param {Error} error + * @param {Boolean} private Set to `true` when you don't want to notify the user. + * @returns error + */ +const catchError = async (client, message, name, error, private = false) => { if (!name) return console.log("[catchError] Please specify the name of the command or function."); if (!error) return console.log("[catchError] Please forward any errors that have occurred."); @@ -34,16 +38,6 @@ const catchError = async (client, message, name, error) => { const whiteColor = ansiColor(15, "foreground"); const redBackground = ansiColor(9, "background"); - const dateTime = (date) => { - const day = date.getDay(); - const month = date.getMonth(); - const year = date.getFullYear(); - const hours = date.getHours(); - const minutes = date.getMinutes(); - const seconds = date.getSeconds(); - return "\u001b[1m[" + day + "-" + month + "-" + year + "." + hours + ":" + minutes + ":" + seconds + "]\u001b[0m" - } - if (message) { const ping = Date.now() - message.createdTimestamp; const api = Math.round(client.ws.ping); @@ -63,28 +57,28 @@ const catchError = async (client, message, name, error) => { .setTimestamp() if ((message.author && message.author.id) === client.user.id) { - message.edit({ + if (!private) message.edit({ "content": null, "embeds": [catchErrorEmbed], "ephemeral": true }); } else { try { - message.channel.send({ + if (!private) message.channel.send({ "content": null, "embeds": [catchErrorEmbed], "ephemeral": true }); } catch { try { - message.send({ + if (!private) message.send({ "content": null, "embeds": [catchErrorEmbed], "ephemeral": true }); } catch { try { - await message.editReply({ + if (!private) await message.editReply({ "content": null, "embeds": [catchErrorEmbed], "ephemeral": true @@ -92,7 +86,7 @@ const catchError = async (client, message, name, error) => { } catch (err) { logGenerator("catch", err) - console.group(dateTime(new Date()) + " :: " + redBackground + whiteColor + boldStyle + "Catch Error" + clearStyle); + console.group("\u001b[1m[" + timeConsole(new Date()) + "]\u001b[0m :: " + redBackground + whiteColor + boldStyle + "Catch Error" + clearStyle); console.group(boldStyle + "Full Error:" + clearStyle); console.error(err); console.groupEnd(); @@ -108,7 +102,7 @@ const catchError = async (client, message, name, error) => { logGenerator("catch", error); - console.group(dateTime(new Date()) + " :: " + redBackground + whiteColor + boldStyle + "Catch Error" + clearStyle); + console.group("\u001b[1m[" + timeConsole(new Date()) + "]\u001b[0m :: " + redBackground + whiteColor + boldStyle + "Catch Error" + clearStyle); console.group(boldStyle + "Full Error:" + clearStyle); console.error(error); console.groupEnd(); @@ -116,25 +110,28 @@ const catchError = async (client, message, name, error) => { console.info(boldStyle + "Discord.js:" + clearStyle + " v" + discord.version); console.info(boldStyle + "Node.js: " + clearStyle + process.version); console.groupEnd(); + return error; } +/** + * For quarantine logs as a temporary file + * + * @param {String} name The name of the quarantined file or process name, e.g. **process**, **error**, **warn** etc. + * @param {String} info log data + * @example + * logGenerator("debug", "[WS => Shard 0] [HeartbeatTimer] Sending a heartbeat.") + */ const logGenerator = (name, info) => { if (!name) return console.log("[LogGenerator] No name provided!"); if (!info) return console.log("[LogGenerator] No info provided!"); + const date = new Date(); + const at = timeConsole(date, "date"); + const when = timeConsole(date, "time"); const directory = "./source/logs/"; if (!existsSync(directory)) mkdirSync(directory); - const date = new Date(); - const year = date.getFullYear(); - const month = date.getMonth(); - const day = date.getDate(); - const hours = date.getHours(); - const minutes = date.getMinutes(); - const seconds = date.getSeconds(); - const at = year + "-" + month + "-" + day; - const when = hours + ":" + minutes + ":" + seconds; const file = createWriteStream(directory + name + "_" + at + ".log", { "flags": "a" }); @@ -142,7 +139,7 @@ const logGenerator = (name, info) => { file.write(format("[%s]: %s\n", when, info)); // After finishing the process, the new line will help to be easier to read. - ["exit", "SIGINT", "SIGUSR1", "SIGUSR2", "uncaughtException", "SIGTERM"].forEach((eventType) => { + ["exit", "SIGINT", "SIGUSR1", "SIGUSR2", "SIGTERM"].forEach((eventType) => { process.on(eventType, () => { process.stdin.resume(); file.write("\n"); @@ -151,9 +148,28 @@ const logGenerator = (name, info) => { }); } +/** + * Calculated as the current time and tailored to the console time. + * + * @param {Date} date time of occurrence + * @param {String} format Three options are supported: **full**, **date** and **time**. + */ +const timeConsole = (date, format = "full") => { + const day = date.getDay(); + const month = date.getMonth(); + const year = date.getFullYear(); + const hours = date.getHours(); + const minutes = date.getMinutes(); + const seconds = date.getSeconds(); + + if (format === "full") return year + "-" + month + "-" + day + "." + hours + ":" + minutes + ":" + seconds; + if (format === "date") return year + "-" + month + "-" + day; + if (format === "time") return hours + ":" + minutes + ":" + seconds; +}; + module.exports = { - asciiArt, ansiColor, catchError, - logGenerator + logGenerator, + timeConsole } \ No newline at end of file diff --git a/source/utils/databaseUtils.js b/source/utils/databaseUtils.js index d0f3c9c8..00d2cf72 100644 --- a/source/utils/databaseUtils.js +++ b/source/utils/databaseUtils.js @@ -2,13 +2,22 @@ const { EmbedBuilder } = require("discord.js"); const { getDatabase, ref, child, set, remove, update } = require("firebase/database"); const { catchError } = require("./consoleUtils"); +/** + * A chat system that supports both mentions and general discussions. + * This requires a database to be able to add or delete words in real time. + * + * @param {Client} client + * @param {Message} message + * @param {Boolean} mentioned if mentioned + * @param {String} args User Message + */ const chatSystem = async (client, message, mentioned, args) => { const childRef = ref(getDatabase(), "projects/shioru"); const argument = message.content.replace(/^<@!?\d{1,20}> ?/i, ""); // When the bot calls and asks some questions. if (argument) { - message.channel.sendTyping(); + await message.channel.sendTyping(); try { const chatSnapshot = client.api.chat; @@ -81,7 +90,7 @@ const chatSystem = async (client, message, mentioned, args) => { message.channel.send(answerScript); } } else { - set(child(childRef, "chat"), { + await set(child(childRef, "chat"), { "prompts": [ ["สวัสดี"] ], @@ -93,9 +102,8 @@ const chatSystem = async (client, message, mentioned, args) => { ], "commands": [[]], "script": [[]] - }).then(() => { - chatSystem(client, message, args); }); + chatSystem(client, message, mentioned, args); } } catch (error) { catchError(client, message, "chatSystem", error); @@ -114,11 +122,10 @@ const chatSystem = async (client, message, mentioned, args) => { return message.channel.send(tagsSnapshot[randomWords]); } else { - set(child(childRef, "chat/tags"), [ + await set(child(childRef, "chat/tags"), [ client.translate.utils.databaseUtils.was_mentioned - ]).then(() => { - chatSystem(client, message, args); - }); + ]); + chatSystem(client, message, args); } } catch (error) { catchError(client, message, "chatSystem", error); @@ -126,13 +133,22 @@ const chatSystem = async (client, message, mentioned, args) => { } }; -const levelSystem = async (client, message, method, argument, amount) => { +/** + * A level system that uses a database to store import and export + * + * @param {Client} client + * @param {Message} message + * @param {String} method **GET**, **GET/ALL**, **POST**, **PUT** or **DELETE** + * @param {GuildMember} member Optional: Members within the guild who wish to change their information + * @param {String} amount Optional: The desired amount will change the value. + * @param {String} type Optional: **exp** or **level** + * @returns + */ +const levelSystem = async (client, message, method, { member = "", amount = 1, type = "exp" } = {}) => { if (!client) return console.log("[levelSystem] Please configure CLIENT for localization (required)."); if (!message) return console.log("[levelSystem] Please configure MESSAGE to make notifications and receive important basic information (required)."); - if (!method) return console.log("[levelSystem] Please specify a method to continue. (required)."); + if (!method) return console.log("[levelSystem] Please specify a METHOD to continue. (required)."); - const authorID = message.author ? message.author.id : message.user.id; - const userID = method.includes(["GET", "DELETE"]) ? argument : authorID; const guildRef = child(ref(getDatabase(), "projects/shioru/guilds"), message.guild.id); const guildUserRef = child(ref(getDatabase(), "projects/shioru/users"), userID); const guildUserSnapshot = client.api.users[userID]; @@ -152,7 +168,7 @@ const levelSystem = async (client, message, method, argument, amount) => { return channel; } } else { - await set(child(guildRef, "notification/alert"), true); + await set(child(guildRef, "notification/alert"), false); notification(msg); } @@ -168,8 +184,8 @@ const levelSystem = async (client, message, method, argument, amount) => { if (exp >= nextEXP) { const alert = await notification(message); - const authorUsername = message.user.username; - const authorAvatar = message.user.displayAvatarURL(); + const authorUsername = message.member ? message.member.username : message.user.username; + const authorAvatar = message.member ? message.member.displayAvatarURL() : message.user.displayAvatarURL(); const levelSystemEmbed = new EmbedBuilder() .setTitle(client.translate.utils.databaseUtils.level_up.replace("%s1", authorUsername).replace("%s2", level)) .setColor("Yellow") @@ -204,10 +220,13 @@ const levelSystem = async (client, message, method, argument, amount) => { } return GETData; case "POST": - if (!argument) return console.log("[levelSystem/POST] Please specify the amount of experience."); + if (!amount) return console.log("[levelSystem/POST] Please specify the amount of experience."); - update(child(guildUserRef, "leveling"), { - "exp": (exp += argument) + if (type === "exp") update(child(guildUserRef, "leveling"), { + "exp": (exp += amount) + }); + if (type === "level") update(child(guildUserRef, "leveling"), { + "level": (level += amount) }); break; case "PUT": @@ -216,9 +235,12 @@ const levelSystem = async (client, message, method, argument, amount) => { let alert = await notification(message); try { - await update(child(child(ref(getDatabase(), "projects/shioru/users"), argument), "leveling"), { + if (type === "exp") await update(child(child(ref(getDatabase(), "projects/shioru/users"), member), "leveling"), { "exp": amount }); + if (type === "level") await update(child(child(ref(getDatabase(), "projects/shioru/users"), member), "leveling"), { + "level": amount + }); PUTStatus = "success"; } catch { PUTStatus = "error"; @@ -234,17 +256,16 @@ const levelSystem = async (client, message, method, argument, amount) => { case "DELETE": let DELETEStatus = ""; - if (!argument) return console.log("[levelSystem/DELETE] Please enter the user ID you wish to delete experience data for."); + if (!member) return console.log("[levelSystem/DELETE] Please enter the user ID you wish to delete experience data for."); if (!guildUserSnapshot.leveling) return DELETEStatus = "missing"; try { - await remove(child(child(ref(getDatabase(), "projects/shioru/users"), argument), "leveling")); + await remove(child(child(ref(getDatabase(), "projects/shioru/users"), member), "leveling")); DELETEStatus = "success"; } catch { DELETEStatus = "error"; } return DELETEStatus; - default: console.log("[levelSystem/METHOD] " + method + " is not a valid method."); } } else { await set(guildUserRef, { @@ -259,127 +280,119 @@ const levelSystem = async (client, message, method, argument, amount) => { } }); - levelSystem(client, message, method, argument, amount); + levelSystem(client, message, method, member, amount, type); } } -const settingsData = (client, guild, exports, callback) => { +/** + * A wizard to set up information in the database for each guild and user. + * + * @param {Client} client + * @param {Guild} guild + */ +const settingsData = (client, guild) => { + const guildSnapshot = client.api.guilds[guild.id]; const guildRef = child(ref(getDatabase(), "projects/shioru/guilds"), guild.id); - - const notificationVerify = (guildRef, guildSnapshot) => { - const notificationList = [ - "alert", - "channelCreate", - "channelDelete", - "channelPinsUpdate", - "channelUpdate", - "emojiCreate", - "emojiDelete", - "emojiUpdate", - "guildBanAdd", - "guildBanRemove", - "guildIntegrationsUpdate", - "guildMemberAdd", - "guildMemberRemove", - "guildMembersChunk", - "guildUnavailable", - "inviteCreate", - "inviteDelete", - "roleCreate", - "roleDelete", - "roleUpdate", - "stageInstanceCreate", - "stageInstanceDelete", - "stageInstanceUpdate", - "stickerCreate", - "stickerDelete", - "stickerUpdate", - "threadCreate", - "threadDelete", - "threadUpdate", - "webhookUpdate" - ]; - - for (const notificationName in notificationList) { - const position = notificationList.at(-1); - - if (guildSnapshot.notification) { - const snapshot = guildSnapshot.notification[notificationName]; - - if (typeof snapshot === "undefined") { - set(child(child(guildRef, "notification"), notificationName), false); - } - if (typeof snapshot !== "boolean") { - set(child(child(guildRef, "notification"), notificationName), snapshot ? true : false); - } - if (notificationName === position) { - return settingsData(client, guild, exports, callback); - } - } else { - set(child(guildRef, "notification"), { - "alert": false, - "channelCreate": false, - "channelDelete": false, - "channelPinsUpdate": false, - "channelUpdate": false, - "emojiCreate": false, - "emojiDelete": false, - "emojiUpdate": false, - "guildBanAdd": false, - "guildBanRemove": false, - "guildIntegrationsUpdate": false, - "guildMemberAdd": false, - "guildMemberRemove": false, - "guildMembersChunk": false, - "guildUnavailable": false, - "inviteCreate": false, - "inviteDelete": false, - "roleCreate": false, - "roleDelete": false, - "roleUpdate": false, - "stageInstanceCreate": false, - "stageInstanceDelete": false, - "stageInstanceUpdate": false, - "stickerCreate": false, - "stickerDelete": false, - "stickerUpdate": false, - "threadCreate": false, - "threadDelete": false, - "threadUpdate": false, - "webhookUpdate": false - }).then(() => { - return settingsData(client, guild, exports, callback); - }); - } - } - }; - - if (!client.api.guilds[guild.id]) { + const notificationList = [ + "alert", + "channelCreate", + "channelDelete", + "channelPinsUpdate", + "channelUpdate", + "emojiCreate", + "emojiDelete", + "emojiUpdate", + "guildBanAdd", + "guildBanRemove", + "guildIntegrationsUpdate", + "guildMemberAdd", + "guildMemberRemove", + "guildMembersChunk", + "guildUnavailable", + "inviteCreate", + "inviteDelete", + "roleCreate", + "roleDelete", + "roleUpdate", + "stageInstanceCreate", + "stageInstanceDelete", + "stageInstanceUpdate", + "stickerCreate", + "stickerDelete", + "stickerUpdate", + "threadCreate", + "threadDelete", + "threadUpdate", + "webhookUpdate" + ]; + + if (guildSnapshot) { + if (!guildSnapshot.joinedAt) set(child(guildRef, "joinedAt"), guild.joinedAt ?? null); + if (!guildSnapshot.createdAt) set(child(guildRef, "createdAt"), guild.createdAt ?? null); + if (!guildSnapshot.description || guildSnapshot.description !== guild.description) set(child(guildRef, "description"), guild.description ?? ""); + if (!guildSnapshot.iconURL || guildSnapshot.iconURL !== guild.iconURL()) set(child(guildRef, "iconURL"), guild.iconURL() ?? ""); + if (!guildSnapshot.language) set(child(guildRef, "language"), client.config.language.code ?? "en"); + if (!guildSnapshot.memberCount || guildSnapshot.memberCount !== guild.memberCount) set(child(guildRef, "memberCount"), guild.memberCount ?? 0); + if (!guildSnapshot.name || guildSnapshot.name !== guild.name) set(child(guildRef, "name"), guild.name ?? ""); + if (!guildSnapshot.verified || guildSnapshot.verified !== guild.verified) set(child(guildRef, "verified"), guild.verified ?? false); + } else { set(guildRef, { - "language": client.config.language.code + "joinedAt": guild.joinedAt ?? null, + "createdAt": guild.createdAt ?? null, + "description": guild.description ?? "", + "iconURL": guild.iconURL() ?? "", + "language": client.config.language.code ?? "en", + "memberCount": guild.memberCount ?? 0, + "name": guild.name ?? "", + "verified": guild.verified ?? false }); - - return notificationVerify(guildRef, client.api.guilds[guild.id]); } - const guildSnapshot = client.api.guilds[guild.id]; + if (guildSnapshot.notification) { + for (const notificationIndex in notificationList) { + const notificationName = notificationList[notificationIndex]; + const notificationSnapshot = guildSnapshot.notification[notificationName]; - if (!guildSnapshot.language) { - return set(child(guildRef, "language"), client.config.language.code).then(() => settingsData(client, guild, exports, callback)); - } - if (!guildSnapshot.notification) { - return notificationVerify(guildRef, guildSnapshot); + if (typeof notificationSnapshot === "undefined") set(child(child(guildRef, "notification"), notificationName), false); + if (typeof notificationSnapshot !== "boolean") set(child(child(guildRef, "notification"), notificationName), notificationSnapshot ? true : false); + } + } else { + set(child(guildRef, "notification"), { + "alert": false, + "channelCreate": false, + "channelDelete": false, + "channelPinsUpdate": false, + "channelUpdate": false, + "emojiCreate": false, + "emojiDelete": false, + "emojiUpdate": false, + "guildBanAdd": false, + "guildBanRemove": false, + "guildIntegrationsUpdate": false, + "guildMemberAdd": false, + "guildMemberRemove": false, + "guildMembersChunk": false, + "guildUnavailable": false, + "inviteCreate": false, + "inviteDelete": false, + "roleCreate": false, + "roleDelete": false, + "roleUpdate": false, + "stageInstanceCreate": false, + "stageInstanceDelete": false, + "stageInstanceUpdate": false, + "stickerCreate": false, + "stickerDelete": false, + "stickerUpdate": false, + "threadCreate": false, + "threadDelete": false, + "threadUpdate": false, + "webhookUpdate": false + }); } - notificationVerify(guildRef, guildSnapshot); - client.config.language.code = guildSnapshot.language; client.translate = require("../languages/" + guildSnapshot.language + ".json"); - - if (!client.temp.set) { - client.temp.set = 1; - return exports(client, callback); - } } module.exports = { diff --git a/source/utils/miscUtils.js b/source/utils/miscUtils.js index 15d45c94..0125d1c5 100644 --- a/source/utils/miscUtils.js +++ b/source/utils/miscUtils.js @@ -147,6 +147,31 @@ const remainingTime = (timeUntil) => { return time; } +/** + * This helps in reducing the length of numbers from the thousands and above. + * To make it easier to read and keep statistics. + * + * @param {Number} number The number want to convert + * @param {Number} digits The number of decimals to be stored. + * @returns A string of converted numbers: e.g. **12.34k** + * @example currencyFormatter(12345, 2); // => 12.34k + */ +const currencyFormatter = (number, digits) => { + const lookup = [ + { "value": 1, "symbol": "" }, + { "value": 1e3, "symbol": "k" }, + { "value": 1e6, "symbol": "M" }, + { "value": 1e9, "symbol": "G" }, + { "value": 1e12, "symbol": "T" }, + { "value": 1e15, "symbol": "P" }, + { "value": 1e18, "symbol": "E" } + ]; + const regex = /\.0+$|(\.[0-9]*[1-9])0+$/; + const item = lookup.slice().reverse().find((item) => number >= item.value); + + return item ? (number / item.value).toFixed(digits).replace(regex, "$1") + item.symbol : "0"; + } + module.exports = { containsLink, containsDiscordInvite, @@ -154,5 +179,6 @@ module.exports = { randomInt, isHex, timeFormat, - remainingTime + remainingTime, + currencyFormatter };