From 4aa0cfe9fad623d8027ce68648947dcba5795d0e Mon Sep 17 00:00:00 2001 From: "Jonny_Bro (Nikita)" Date: Mon, 18 Nov 2024 19:19:24 +0500 Subject: [PATCH] refactor some helpers --- README.md | 1 + base/Client.js | 4 +- commands/Fun/tictactoe.js | 2 - helpers/birthdays.js | 156 ++++++++++++-------------------------- helpers/checkReminds.js | 67 ++-------------- helpers/extenders.js | 93 ++++++++++++++++------- helpers/functions.js | 5 -- helpers/logger.js | 24 ++---- helpers/mee6-api.js | 112 --------------------------- helpers/tictactoe.js | 42 ++++------ 10 files changed, 142 insertions(+), 364 deletions(-) delete mode 100644 helpers/mee6-api.js diff --git a/README.md b/README.md index 34ccd605..9e27a0ca 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ If you want to contribute, feel free to fork this repo and making a pull request ## TODO +* [ ] Refactor [tictactoe](./helpers/tictactoe.js). * [ ] Finish and release *dashboard-core* submodule. ## License diff --git a/base/Client.js b/base/Client.js index 8e1f042d..e99fff88 100644 --- a/base/Client.js +++ b/base/Client.js @@ -105,8 +105,8 @@ class JaBaClient extends Client { .then(() => { this.logger.log("Connected to the MongoDB database."); }) - .catch(err => { - this.logger.error(`Unable to connect to the MongoDB database.\nError: ${err}`); + .catch(e => { + this.logger.error(`Unable to connect to the MongoDB database.\nError: ${e.message}\n${e.stack}`); }); this.login(this.config.token); diff --git a/commands/Fun/tictactoe.js b/commands/Fun/tictactoe.js index 0a874081..ccfb350e 100644 --- a/commands/Fun/tictactoe.js +++ b/commands/Fun/tictactoe.js @@ -41,8 +41,6 @@ class TicTacToe extends BaseCommand { async execute(client, interaction) { const winner = await tictactoe(interaction, { resultBtn: true, - embedColor: client.config.embed.color, - embedFoot: client.config.embed.footer, }); const memberData = await client.getMemberData(winner.id, interaction.guildId); diff --git a/helpers/birthdays.js b/helpers/birthdays.js index e943b142..fd8ee570 100644 --- a/helpers/birthdays.js +++ b/helpers/birthdays.js @@ -4,122 +4,62 @@ const { CronJob } = require("cron"); * * @param {import("../base/Client")} client */ -module.exports.init = async client => { - const cronjob = new CronJob("0 5 * * *", - async function () { - // Iterate over all guilds the bot is in - for (const guild of client.guilds.cache.values()) { - try { - console.log(`Checking birthdays for "${guild.name}"`); - - const guildData = await client.getGuildData(guild.id); - const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null; - - if (channel) { - const date = new Date(); - const currentDay = date.getDate(); - const currentMonth = date.getMonth() + 1; - const currentYear = date.getFullYear(); - - const users = await client.usersData.find({ birthdate: { $gt: 1 } }); - - for (const user of users) { - // Check if the user is in the guild - if (!guild.members.cache.has(user.id)) continue; - - const userDate = user.birthdate > 9999999999 ? new Date(user.birthdate * 1000) : new Date(user.birthdate); - const day = userDate.getDate(); - const month = userDate.getMonth() + 1; - const year = userDate.getFullYear(); - const age = currentYear - year; - - // Check if it's the user's birthday - if (currentMonth === month && currentDay === day) { - const embed = client.embed({ - author: client.user.getUsername(), - fields: [ - { - name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language), - value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", { - user: user.id, - age: `**${age}** ${client.functions.getNoun( - age, - client.translate("misc:NOUNS:AGE:1", null, guildData.language), - client.translate("misc:NOUNS:AGE:2", null, guildData.language), - client.translate("misc:NOUNS:AGE:5", null, guildData.language), - )}`, - }, guildData.language), - }, - ], - }); - - await channel.send({ embeds: [embed] }).then(m => m.react("🎉")); - } - } - } - } catch (err) { - if (err.code === 10003) console.log(`No channel found for ${guild.name}`); - else console.error(`Error processing guild "${guild.name}":`, err); - } - } - }, - null, - true, - ); - - cronjob.start(); -}; - -/** - * - * @param {import("../base/Client")} client - */ -module.exports.run = async client => { +async function checkBirthdays(client) { for (const guild of client.guilds.cache.values()) { - const guildData = await client.getGuildData(guild.id); - const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null; + try { + const guildData = await client.getGuildData(guild.id); + const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null; - if (channel) { - const date = new Date(); - const currentDay = date.getDate(); - const currentMonth = date.getMonth() + 1; - const currentYear = date.getFullYear(); + if (channel) { + const date = new Date(); + const currentDay = date.getDate(); + const currentMonth = date.getMonth() + 1; + const currentYear = date.getFullYear(); - const users = await client.usersData.find({ birthdate: { $gt: 1 } }); + const users = await client.usersData.find({ birthdate: { $gt: 1 } }); - for (const user of users) { - // Check if the user is in the guild - if (!guild.members.cache.has(user.id)) continue; + for (const user of users) { + if (!guild.members.cache.has(user.id)) continue; - const userDate = new Date(user.birthdate * 1000); - const day = userDate.getDate(); - const month = userDate.getMonth() + 1; - const year = userDate.getFullYear(); - const age = currentYear - year; + const userDate = user.birthdate > 9999999999 ? new Date(user.birthdate * 1000) : new Date(user.birthdate); + const day = userDate.getDate(); + const month = userDate.getMonth() + 1; + const year = userDate.getFullYear(); + const age = currentYear - year; - // Check if it's the user's birthday - if (currentMonth === month && currentDay === day) { - const embed = client.embed({ - author: client.user.getUsername(), - fields: [ - { - name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language), - value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", { - user: user.id, - age: `**${age}** ${client.functions.getNoun( - age, - client.translate("misc:NOUNS:AGE:1", null, guildData.language), - client.translate("misc:NOUNS:AGE:2", null, guildData.language), - client.translate("misc:NOUNS:AGE:5", null, guildData.language), - )}`, - }, guildData.language), - }, - ], - }); + if (currentMonth === month && currentDay === day) { + const embed = client.embed({ + author: client.user.getUsername(), + fields: [ + { + name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language), + value: client.translate( + "economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", + { + user: user.id, + age: `**${age}** ${client.functions.getNoun( + age, + client.translate("misc:NOUNS:AGE:1", null, guildData.language), + client.translate("misc:NOUNS:AGE:2", null, guildData.language), + client.translate("misc:NOUNS:AGE:5", null, guildData.language), + )}`, + }, + guildData.language, + ), + }, + ], + }); - await channel.send({ embeds: [embed] }).then(m => m.react("🎉")); + await channel.send({ embeds: [embed] }).then(m => m.react("🎉")); + } } } + } catch (e) { + if (e.code === 10003) console.log(`No channel found for ${guild.name}`); + else console.error(`Error processing birthdays for guild "${guild.name}":`, e); } } -}; +} + +module.exports.init = async client => new CronJob("0 5 * * *", checkBirthdays(client), null, true, "Europe/Moscow"); +module.exports.run = async client => await checkBirthdays(client); diff --git a/helpers/checkReminds.js b/helpers/checkReminds.js index b7e0adaa..7f0c123b 100644 --- a/helpers/checkReminds.js +++ b/helpers/checkReminds.js @@ -2,67 +2,7 @@ * * @param {import("../base/Client")} client */ -module.exports.init = function (client) { - client.usersData.find({ reminds: { $gt: [] } }).then(users => { - for (const user of users) { - if (!client.users.cache.has(user.id)) client.users.fetch(user.id); - - client.databaseCache.usersReminds.set(user.id, user); - } - }); - - setInterval(async function () { - client.databaseCache.usersReminds.forEach(async user => { - const cachedUser = client.users.cache.get(user.id); - - if (cachedUser) { - const dateNow = Math.floor(Date.now() / 1000), - reminds = user.reminds, - mustSent = reminds.filter(r => r.sendAt < dateNow); - - if (mustSent.length > 0) { - mustSent.forEach(r => { - const embed = client.embed({ - author: client.translate("general/remindme:EMBED_TITLE"), - fields: [ - { - name: client.translate("general/remindme:EMBED_CREATED"), - value: ``, - inline: true, - }, - { - name: client.translate("general/remindme:EMBED_TIME"), - value: ``, - inline: true, - }, - { - name: client.translate("common:MESSAGE"), - value: r.message, - }, - ], - }); - - cachedUser.send({ - embeds: [embed], - }); - }); - - user.reminds = user.reminds.filter(r => r.sendAt >= dateNow); - - await user.save(); - - if (user.reminds.length === 0) client.databaseCache.usersReminds.delete(user.id); - } - } - }); - }, 1000); -}; - -/** - * - * @param {import("../base/Client")} client - */ -module.exports.run = function (client) { +async function checkReminds(client) { client.usersData.find({ reminds: { $gt: [] } }).then(users => { for (const user of users) { if (!client.users.cache.has(user.id)) client.users.fetch(user.id); @@ -114,4 +54,7 @@ module.exports.run = function (client) { } } }); -}; +} + +module.exports.init = async client => setInterval(async () => await checkReminds(client), 1000); +module.exports.run = async client => await checkReminds(client); diff --git a/helpers/extenders.js b/helpers/extenders.js index 06561b69..c8fe7ccd 100644 --- a/helpers/extenders.js +++ b/helpers/extenders.js @@ -1,71 +1,106 @@ const { Message, BaseInteraction, User, GuildMember } = require("discord.js"); + +/** + * + * @param {Message|BaseInteraction} context + * @returns {string} Locale + */ +function getLocale(context) { + return context.data?.guild?.language; +} + +/** + * + * @param {import("../base/Client")} client + * @param {string} key + * @param {any[]} args + * @param {string} locale + * @returns {string} Localized string + */ +function translate(client, key, args, locale) { + const language = client.translations.get(locale || "en-US"); + if (!language) throw "Can't find given localization."; + + return language(key, args); +} + +/** + * + * @param {import("../base/Client")} client + * @param {string} prefixEmoji + * @param {string} message + * @returns {string} Localized message + */ +function formatReply(client, prefixEmoji, message) { + return prefixEmoji ? `${client.customEmojis[prefixEmoji]} | ${message}` : message; +} + +/** + * + * @param {Message|BaseInteraction} context + * @param {string} key + * @param {any[]} args + * @param {string} options + * @returns + */ +async function replyTranslated(context, key, args, options = {}) { + const locale = options.locale || getLocale(context) || "en-US"; + const translated = translate(context.client, key, args, locale); + const content = formatReply(context.client, options.prefixEmoji, translated); + + if (options.edit) return context.editReply ? await context.editReply({ content, ephemeral: options.ephemeral || false }) : await context.edit({ content, allowedMentions: { repliedUser: options.mention || false } }); + else return context.editReply ? await context.reply({ content, ephemeral: options.ephemeral || false }) : await context.reply({ content, allowedMentions: { repliedUser: options.mention || false } }); +} + User.prototype.getUsername = function () { return this.discriminator === "0" ? this.username : this.tag; }; GuildMember.prototype.getUsername = function () { - return this.user.discriminator === "0" ? this.user.username : this.user.tag; + return this.user.getUsername(); }; BaseInteraction.prototype.getLocale = function () { - return this.data?.guild?.language; + return getLocale(this); }; BaseInteraction.prototype.translate = function (key, args, locale) { - const language = this.client.translations.get(this.getLocale() || locale || "en-US"); - if (!language) throw "Interaction: Invalid language set in data."; - - return language(key, args); + return translate(this.client, key, args, locale || this.getLocale()); }; BaseInteraction.prototype.replyT = async function (key, args, options = {}) { - const translated = this.translate(key, args, this.getLocale() || options.locale || "en-US"); - const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated; - - if (options.edit) return this.editReply({ content: string, ephemeral: options.ephemeral || false }); - else return this.reply({ content: string, ephemeral: options.ephemeral || false }); + return await replyTranslated(this, key, args, options); }; BaseInteraction.prototype.success = async function (key, args, options = {}) { options.prefixEmoji = "success"; - - return this.replyT(key, args, options); + return await this.replyT(key, args, options); }; BaseInteraction.prototype.error = async function (key, args, options = {}) { options.prefixEmoji = "error"; - - return this.replyT(key, args, options); + return await this.replyT(key, args, options); }; Message.prototype.getLocale = function () { - return this.data?.guild?.language; + return getLocale(this); }; Message.prototype.translate = function (key, args, locale) { - const language = this.client.translations.get(this.getLocale() || locale || "en-US"); - if (!language) throw "Message: Invalid language set in data."; - - return language(key, args); + return translate(this.client, key, args, locale || this.getLocale()); }; Message.prototype.replyT = async function (key, args, options = {}) { - const translated = this.translate(key, args, this.getLocale() || options.locale || "en-US"); - const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated; - - if (options.edit) return this.edit({ content: string, allowedMentions: { repliedUser: options.mention ? true : false } }); - else return this.reply({ content: string, allowedMentions: { repliedUser: options.mention ? true : false } }); + return await replyTranslated(this, key, args, options); }; Message.prototype.success = async function (key, args, options = {}) { options.prefixEmoji = "success"; - - return this.replyT(key, args, options); + return await this.replyT(key, args, options); }; Message.prototype.error = async function (key, args, options = {}) { options.prefixEmoji = "error"; - - return this.replyT(key, args, options); + return await this.replyT(key, args, options); }; diff --git a/helpers/functions.js b/helpers/functions.js index c0f9b047..cc39816d 100644 --- a/helpers/functions.js +++ b/helpers/functions.js @@ -1,9 +1,4 @@ const moment = require("moment"); -// const { GlobalFonts } = require("@napi-rs/canvas"), -// const { resolve } = require("path"); - -// GlobalFonts.registerFromPath(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), "RubikMonoOne"); -// GlobalFonts.registerFromPath(resolve("./assets/fonts/KeepCalm-Medium.ttf"), "KeepCalm"); module.exports = { /** diff --git a/helpers/logger.js b/helpers/logger.js index 3414cde8..86f02925 100644 --- a/helpers/logger.js +++ b/helpers/logger.js @@ -27,38 +27,26 @@ function format(tDate) { module.exports = class Logger { static log(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${bgBlue("LOG")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${bgBlue("LOG")} ${content}`); } static warn(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${black.bgYellow("WARN")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${black.bgYellow("WARN")} ${content}`); } static error(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${black.bgRed("ERROR")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${black.bgRed("ERROR")} ${content}`); } static debug(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${green("DEBUG")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${green("DEBUG")} ${content}`); } static cmd(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${black.bgWhite("CMD")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${black.bgWhite("CMD")} ${content}`); } static ready(content) { - const date = `[${format(new Date(Date.now()))}]:`; - - return console.log(`${date} ${black.bgGreen("READY")} ${content}`); + return console.log(`[${format(new Date(Date.now()))}]: ${black.bgGreen("READY")} ${content}`); } }; diff --git a/helpers/mee6-api.js b/helpers/mee6-api.js deleted file mode 100644 index 3d8791d1..00000000 --- a/helpers/mee6-api.js +++ /dev/null @@ -1,112 +0,0 @@ -/* eslint-disable no-constant-condition */ -// Thanks to mee6-levels-api =) - -const fetch = require("node-fetch"); - -const fetchMee6 = async args => { - const response = await fetch(`https://mee6.xyz/api/plugins/levels/leaderboard/${args}`).then(res => res.json()); - if (response.statusCode) { - if (response.error && response.error.message) throw new Error(`${response.statusCode}: ${response.error.message}`); - else throw new Error(`${response.statusCode}: ${response.statusMessage}`); - } - - return response; -}; - -class Mee6Api { - /** - * Resolves a guild or user to its ID. - * @param {{id:String}|String} guildOrUser Object to resolve to an ID. - * @returns {String} ID of the guild or user. - */ - static getId(guildOrUser) { - if (typeof guildOrUser === "string") return guildOrUser; - else if (typeof guildOrUser.id === "string") return guildOrUser.id; - else throw new Error("Invalid Id specified."); - } - - /** - * Gets role rewards set for a guild. - * @param {{id:String}|String} guild Guild to get role rewards of. - * @returns {Promise<[Object]>} Role rewards set for this guild. - */ - static async getRoleRewards(guild) { - const guildId = this.getId(guild); - const { role_rewards } = await fetchMee6(`${guildId}?limit=1`); - - return role_rewards.sort((a, b) => a.rank - b.rank).map(({ rank, ...rest }) => ({ ...rest, level: rank })); - } - - /** - * Get a page of the leaderboard of a guild. - * @param {{id:String}|String} guild Guild to get the leaderboard of. - * @param {Number} limit Limit of users to fetch per page. Maximum 1000. - * @param {Number} page Number of pages to skip. - * @returns {Promise<[Object]>} Leaderboard page. - */ - static async getLeaderboardPage(guild, limit = 1000, page = 0) { - const guildId = this.getId(guild); - const { players } = await fetchMee6(`${guildId}?limit=${limit}&page=${page}`); - return players.map((user, index) => { - const { id, level, username, discriminator, avatar, message_count: messageCount } = user; - const avatarUrl = `https://cdn.discordapp.com/avatars/${id}/${avatar}`; - const [userXp, levelXp, totalXp] = user.detailed_xp; - - return { - id, - level, - username, - discriminator, - avatarUrl, - messageCount, - tag: `${username}#${discriminator}`, - xp: { userXp, levelXp, totalXp }, - rank: limit * page + index + 1, - }; - }); - } - - /** - * Get the leaderboard of a guild. - * @param {{id:String}|String} guild Guild to get the leaderboard of. - * @returns {Promise<[Object]>} Leaderboard of the guild. - */ - static async getLeaderboard(guild) { - const leaderboard = []; - let pageNumber = 0, - page; - - while (true) { - page = await this.getLeaderboardPage(guild, 1000, pageNumber); - leaderboard.push(...page); - if (page.length < 1000) break; - pageNumber += 1; - } - - return leaderboard; - } - - /** - * Get the XP of an individual user in a guild. - * @param {{id:String}|String} guild Guild the user is in. - * @param {{id:String}|String} user User to get the XP of. - * @returns {Promise} XP information of the user. - */ - static async getUserXp(guild, user) { - const userId = this.getId(user); - let pageNumber = 0, - page, - userInfo; - - while (true) { - page = await this.getLeaderboardPage(guild, 1000, pageNumber); - userInfo = page.find(u => u.id === userId); - if (page.length < 1000 || userInfo) break; - pageNumber += 1; - } - - return userInfo; - } -} - -module.exports = Mee6Api; diff --git a/helpers/tictactoe.js b/helpers/tictactoe.js index 0ead49c5..4df1ae56 100644 --- a/helpers/tictactoe.js +++ b/helpers/tictactoe.js @@ -1,32 +1,22 @@ // Thanks to simply-djs for this =) +// TODO: Refactor this please... const { ButtonBuilder, ActionRowBuilder, ButtonStyle, ComponentType } = require("discord.js"); /** * @param {import("discord.js").ChatInputCommandInteraction} interaction - * @param {Array} options - * slash => Boolean - * - * userSlash => String - * - * resultBtn => Boolean - * - * embedFoot => String - * - * embedColor => HexColor - * - * timeoutEmbedColor => HexColor - * - * xEmoji => (Emoji ID) String - * - * oEmoji => (Emoji ID) String - * - * idleEmoji => (Emoji ID) String + * @param {any[]} options Array with options (everything is optional) + * @param {string} options.userSlash Name of the user option in the interaction + * @param {string} options.embedFooter Game's embed footer + * @param {string} options.embedColor Game's embed color + * @param {string} options.timeoutEmbedColor Game's embed timeout color + * @param {string} options.xEmoji Emoji for X + * @param {string} options.oEmoji Emoji for O + * @param {string} options.idleEmoji Emoji for "nothing" * @returns {Promise} */ async function tictactoe(interaction, options = {}) { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async resolve => { + return new Promise(resolve => { try { const { client } = interaction; let opponent; @@ -71,8 +61,8 @@ async function tictactoe(interaction, options = {}) { }); } - const footer = options.embedFoot ? options.embedFoot : { text: "GLHF" }, - color = options.embedColor || "#075FFF", + const footer = options.embedFooter || client.config.embed.footer, + color = options.embedColor || client.config.embed.color, user = interaction.user ? interaction.user : interaction.author; const acceptEmbed = client.embed({ @@ -93,7 +83,7 @@ async function tictactoe(interaction, options = {}) { let m; if (interaction.commandId) - m = await interaction.reply({ + m = interaction.reply({ content: interaction.translate("fun/tictactoe:INVITE_USER", { opponent: opponent.id, }), @@ -101,7 +91,7 @@ async function tictactoe(interaction, options = {}) { components: [accep], }); else if (!interaction.commandId) - m = await interaction.reply({ + m = interaction.reply({ content: interaction.translate("fun/tictactoe:INVITE_USER", { opponent: opponent.id, }), @@ -637,8 +627,8 @@ async function tictactoe(interaction, options = {}) { }); } }); - } catch (err) { - console.log(`tictactoe | ERROR: ${err.stack}`); + } catch (e) { + console.log("TicTacToe errored:", e); } }); }