From b1d1c5a0ab25bc63e1309ca8180299f17c5f4020 Mon Sep 17 00:00:00 2001 From: "Jonny_Bro (Nikita)" Date: Sun, 7 Apr 2024 15:29:16 +0500 Subject: [PATCH] v4.5.0 --- base/Client.js | 92 ++++++++++++++++--------- commands/Moderation/poll.js | 4 +- commands/Music/nowplaying.js | 2 +- commands/beatrun.ru/courses.js | 90 ++++++++++++++++++++++++ helpers/functions.js | 82 +++++++++++----------- languages/en-US/beatrun.ru/courses.json | 11 +++ languages/en-US/common.json | 1 + languages/ru-RU/beatrun.ru/courses.json | 11 +++ languages/ru-RU/common.json | 1 + languages/uk-UA/beatrun.ru/courses.json | 11 +++ languages/uk-UA/common.json | 1 + package.json | 2 +- 12 files changed, 230 insertions(+), 78 deletions(-) create mode 100644 commands/beatrun.ru/courses.js create mode 100644 languages/en-US/beatrun.ru/courses.json create mode 100644 languages/ru-RU/beatrun.ru/courses.json create mode 100644 languages/uk-UA/beatrun.ru/courses.json diff --git a/base/Client.js b/base/Client.js index ef57d45f..7c8bcc5d 100644 --- a/base/Client.js +++ b/base/Client.js @@ -81,7 +81,11 @@ class JaBaClient extends Client { } /** - * Logins into the account and connects to the database + * Initializes the client by logging in with the provided token and connecting to the MongoDB database. + * + * This method is called during the client's startup process to set up the necessary connections and resources. + * + * @returns {Promise} A Promise that resolves when the client is fully initialized. */ async init() { this.login(this.config.token); @@ -100,8 +104,14 @@ class JaBaClient extends Client { } /** - * Loads all commands from directory - * @param {String} dir Directory where commands are located + * Loads all the commands from the specified directory and registers them with the Discord API. + * + * This method is responsible for dynamically loading all the command files from the specified directory, + * creating instances of the corresponding command classes, and registering the commands with the Discord API. + * It also handles any additional setup or initialization required by the loaded commands. + * + * @param {string} dir - The directory path where the command files are located. + * @returns {Promise} A Promise that resolves when all the commands have been loaded and registered. */ async loadCommands(dir) { const rest = new REST().setToken(this.config.token), @@ -147,17 +157,19 @@ class JaBaClient extends Client { } /** - * @returns {String} Bot's default language + * Returns the default language from the list of available languages. + * @returns {Language} The default language. */ get defaultLanguage() { return this.languages.find(language => language.default); } /** - * Translates from a key to language - * @param {String} key Key - * @param {Array} args Arguments for translation - * @param {String} locale Language + * Translates a key using the specified locale, or the default language if no locale is provided. + * @param {string} key The translation key to look up. + * @param {any[]} args Any arguments to pass to the translation function. + * @param {string} [locale=this.defaultLanguage.name] The locale to use for the translation. Defaults to the default language. + * @returns {string} The translated string. */ translate(key, args, locale = this.defaultLanguage.name) { const lang = this.translations.get(locale); @@ -166,9 +178,19 @@ class JaBaClient extends Client { } /** - * Returns an embed created from given data - * @param {Object} data Data for embed - * @returns {import("discord.js").Embed} + * Generates an EmbedBuilder instance with the provided data. + * @param {Object} data - The data to use for the embed. + * @param {string} [data.title] - The title of the embed. + * @param {string} [data.description] - The description of the embed. + * @param {string} [data.thumbnail] - The URL of the thumbnail image for the embed. + * @param {Object[]} [data.fields] - An array of field objects for the embed. + * @param {string} [data.image] - The URL of the image for the embed. + * @param {string} [data.url] - The URL to be used as the embed's hyperlink. + * @param {number} [data.color] - The color of the embed's border. If not provided, the default color from the client's configuration will be used. + * @param {string} [data.footer] - The text to be displayed as the embed's footer. If not provided, the default footer from the client's configuration will be used. + * @param {Date} [data.timestamp] - The timestamp to be displayed in the embed's footer. If not provided, the current timestamp will be used. + * @param {string|Object} [data.author] - The author information for the embed. Can be a string (name) or an object with `name` and/or `iconURL` properties. + * @returns {EmbedBuilder} The generated EmbedBuilder instance. */ embed(data) { const embed = new EmbedBuilder() @@ -193,16 +215,16 @@ class JaBaClient extends Client { if (!data.author || data.author === null) embed.setAuthor(null); else if (typeof data.author === "string") embed.setAuthor({ name: data.author, iconURL: this.user.avatarURL() }); - else if (typeof data.author === "object" && (data.author.iconURL !== null || data.author.iconURL !== undefined)) embed.setAuthor({ name: data.author.name, iconURL: this.user.avatarURL() }); + else if (typeof data.author === "object" && (data.author.iconURL !== null || data.author.iconURL !== undefined)) embed.setAuthor({ name: data.author.name, iconURL: data.author.iconURL }); else embed.setAuthor(data.author); return embed; } /** - * Creates an invite link for guild - * @param {String} guildId Guild ID - * @returns {String} Invite link + * Creates an invite for the specified guild. + * @param {string} guildId - The ID of the guild to create the invite for. + * @returns {Promise} The URL of the created invite, or an error message if no suitable channel was found or the bot lacks the necessary permissions. */ async createInvite(guildId) { const guild = this.guilds.cache.get(guildId), @@ -213,9 +235,10 @@ class JaBaClient extends Client { } /** - * Loads a single command from directory - * @param {String} dir Directory where command is located - * @param {String} file Filename of the command + * Loads a command from the specified directory and file. + * @param {string} dir - The directory containing the command file. + * @param {string} file - The name of the command file (without the .js extension). + * @returns {Promise} A log message indicating the successful loading of the command. */ async loadCommand(dir, file) { const Command = require(path.join(dir, `${file}.js`)); @@ -230,10 +253,10 @@ class JaBaClient extends Client { } /** - * Removes a command from cache - * @param {String} dir Directory where command is located - * @param {String} name Command name - * @returns + * Unloads a command from the specified directory and file. + * @param {string} dir - The directory containing the command file. + * @param {string} name - The name of the command file (without the .js extension). + * @returns {void} This method does not return a value. */ async unloadCommand(dir, name) { delete require.cache[require.resolve(`${dir}${path.sep}${name}.js`)]; @@ -242,8 +265,9 @@ class JaBaClient extends Client { } /** - * Loads all events from directory recursively - * @param {String} dir Directory where events are located + * Loads all event files from the specified directory and its subdirectories. + * @param {string} dir - The directory containing the event files. + * @returns {void} This method does not return a value. */ async loadEvents(dir) { const filePath = path.join(__dirname, dir); @@ -270,9 +294,9 @@ class JaBaClient extends Client { } /** - * Finds or creates a user in the database - * @param {String} userID User ID - * @returns {Promise} Mongoose model + * Finds or creates a user in the database based on the provided user ID. + * @param {string} userID - The ID of the user to find or create. + * @returns {Promise} The user data object, either retrieved from the database or newly created. */ async findOrCreateUser(userID) { let userData = await this.usersData.findOne({ id: userID }); @@ -293,9 +317,11 @@ class JaBaClient extends Client { } /** - * Finds or creates a guild's member in the database - * @param {Array} { id: Member ID, Guild ID } - * @returns {Promise} Mongoose model + * Finds or creates a member in the database based on the provided member ID and guild ID. + * @param {Object} options - The options for finding or creating the member. + * @param {string} options.id - The ID of the member to find or create. + * @param {string} options.guildId - The ID of the guild the member belongs to. + * @returns {Promise} The member data object, either retrieved from the database or newly created. */ async findOrCreateMember({ id: memberID, guildId }) { let memberData = await this.membersData.findOne({ guildID: guildId, id: memberID }); @@ -324,9 +350,9 @@ class JaBaClient extends Client { } /** - * Finds or creates guild in DB - * @param {String} guildId Guild ID - * @returns {Promise} Mongoose model + * Finds or creates a guild in the database based on the provided guild ID. + * @param {string} guildId - The ID of the guild to find or create. + * @returns {Promise} The guild data object, either retrieved from the database or newly created. */ async findOrCreateGuild(guildId) { let guildData = await this.guildsData.findOne({ id: guildId }).populate("members"); diff --git a/commands/Moderation/poll.js b/commands/Moderation/poll.js index 0e623093..b670dd3b 100644 --- a/commands/Moderation/poll.js +++ b/commands/Moderation/poll.js @@ -91,11 +91,11 @@ class Poll extends BaseCommand { author: interaction.translate("moderation/poll:TITLE"), fields: [ { - name: "\u200b", + name: "\u200B", value: question, }, { - name: "\u200b", + name: "\u200B", value: interaction.translate("moderation/poll:REACT", { success: cool.toString(), error: notcool.toString(), diff --git a/commands/Music/nowplaying.js b/commands/Music/nowplaying.js index 84e42798..a23f2077 100644 --- a/commands/Music/nowplaying.js +++ b/commands/Music/nowplaying.js @@ -278,7 +278,7 @@ async function updateEmbed(interaction, queue) { value: progressBar, }, { - name: "\u200b", + name: "\u200B", value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[mode]}\``, }, ], diff --git a/commands/beatrun.ru/courses.js b/commands/beatrun.ru/courses.js new file mode 100644 index 00000000..a2ead701 --- /dev/null +++ b/commands/beatrun.ru/courses.js @@ -0,0 +1,90 @@ +const { SlashCommandBuilder } = require("discord.js"); +const BaseCommand = require("../../base/BaseCommand"), + fetch = require("node-fetch"); + +class Courses extends BaseCommand { + /** + * + * @param {import("../base/Client")} client + */ + constructor(client) { + super({ + command: new SlashCommandBuilder() + .setName("courses") + .setDescription(client.translate("beatrun.ru/courses:DESCRIPTION")) + .setDescriptionLocalizations({ + uk: client.translate("beatrun.ru/courses:DESCRIPTION", null, "uk-UA"), + ru: client.translate("beatrun.ru/courses:DESCRIPTION", null, "ru-RU"), + }) + .setDMPermission(false) + .addStringOption(option => + option + .setName("code") + .setDescription(client.translate("common:CODE")) + .setDescriptionLocalizations({ + uk: client.translate("common:CODE", null, "uk-UA"), + ru: client.translate("common:CODE", null, "ru-RU"), + }) + .setRequired(true), + ), + dirname: __dirname, + ownerOnly: false, + }); + } + + /** + * + * @param {import("../../base/Client")} client + * @param {import("discord.js").ChatInputCommandInteraction} interaction + * @param {Object} data + */ + async execute(client, interaction) { + await interaction.deferReply(); + + const code = interaction.options.getString("code"); + + const response = await fetch(`https://courses.beatrun.ru/api/stats/${code}`).then(res => res.json()); + + if (response.res === 401) return interaction.error("beatrun.ru/courses:NOT_FOUND", null, { ephemeral: true, edit: true }); + + const embed = client.embed({ + title: code, + description: `[${interaction.translate("beatrun.ru/courses:DOWNLOAD")}](https://courses.beatrun.ru/${response.course.path})`, + thumbnail: response.course.mapimg, + url: `https://courses.beatrun.ru/?search=${code}`, + fields: [ + { + name: interaction.translate("beatrun.ru/courses:MAP"), + value: `[${response.course.map}](https://steamcommunity.com/sharedfiles/filedetails/?id=${response.course.mapid})`, + inline: true, + }, + { + name: interaction.translate("beatrun.ru/courses:UPLOADER"), + value: `[${response.course.uploader.name}](https://steamcommunity.com/profiles/${response.course.uploader.userid})`, + inline: true, + }, + { + name: "\u200B", + value: "\u200B", + inline: true, + }, + { + name: interaction.translate("beatrun.ru/courses:DATE"), + value: ``, + inline: true, + }, + { + name: interaction.translate("beatrun.ru/courses:PLAYS"), + value: `${response.course.plays || 0}`, + inline: true, + }, + ], + }); + + interaction.editReply({ + embeds: [embed], + }); + } +} + +module.exports = Courses; diff --git a/helpers/functions.js b/helpers/functions.js index 123ad04d..4941069c 100644 --- a/helpers/functions.js +++ b/helpers/functions.js @@ -1,18 +1,12 @@ const moment = require("moment"); -moment.relativeTimeThreshold("ss", 5); -moment.relativeTimeThreshold("s", 60); -moment.relativeTimeThreshold("m", 60); -moment.relativeTimeThreshold("h", 60); -moment.relativeTimeThreshold("d", 24); -moment.relativeTimeThreshold("M", 12); - module.exports = { /** - * Calls a Callback for Each Element in Collection Asynchronously - * @param {Array} collection Collection - * @param {Function} callback Function to Perform on Collection - * @returns {Promise} + * Asynchronously iterates over a collection and executes a callback function for each item. + * + * @param {any[]} collection - The collection to iterate over. + * @param {(item: any) => Promise} callback - The async callback function to execute for each item in the collection. + * @returns {Promise} A promise that resolves when all items in the collection have been processed. */ async asyncForEach(collection, callback) { const allPromises = collection.map(async key => { @@ -23,10 +17,11 @@ module.exports = { }, /** - * Sorts an Array by Key - * @param {Array} array Array to Sort - * @param {Number} key Key - * @returns {Array} Sorted Array + * Sorts an array by the specified key in ascending order. + * + * @param {any[]} array - The array to sort. + * @param {string} key - The key to sort the array by. + * @returns {any[]} The sorted array. */ sortByKey(array, key) { return array.sort(function (a, b) { @@ -37,9 +32,10 @@ module.exports = { }, /** - * Shuffles the Array - * @param {Array} pArray Array to Shuffle - * @returns {Array} Shuffled Array + * Shuffles the elements of the provided array in-place. + * + * @param {any[]} pArray - The array to shuffle. + * @returns {any[]} The shuffled array. */ shuffle(pArray) { const array = []; @@ -63,10 +59,11 @@ module.exports = { }, /** - * Returns a Random Number Between min (inclusive) And max (inclusive) - * @param {Number} min Min value (inclusive) - * @param {Number} max Max value (inclusive) - * @returns {Number} Integer + * Generates a random integer between the specified minimum and maximum values (inclusive). + * + * @param {number} min - The minimum value (inclusive). + * @param {number} max - The maximum value (inclusive). + * @returns {number} A random integer between min and max. */ randomNum(min, max) { min = Math.ceil(min); @@ -76,12 +73,13 @@ module.exports = { }, /** - * Beautifies the given Date - * @param {import("../base/Client")} client Discord Client - * @param {Date} date Date - * @param {String | null} format Format for Moment - * @param {String} locale Language - * @returns {String} Beautified Date + * Formats a date for the specified client and locale. + * + * @param {Object} client - The client object containing language data. + * @param {string} date - The date to format. + * @param {string} [format=null] - The date format to use. If not provided, the default format for the client's language will be used. + * @param {string} [locale=client.defaultLanguage.name] - The locale to use for formatting the date. + * @returns {string} The formatted date. */ printDate(client, date, format = null, locale = client.defaultLanguage.name) { const languageData = client.languages.find(language => language.name === locale); @@ -91,13 +89,14 @@ module.exports = { }, /** - * Converts the Given Time - * @param {import("../base/Client")} client Discord Client - * @param {String} time Time - * @param {Boolean} type Type (To now = true or from now = false) - * @param {Boolean} prefix Include Prefix - * @param {String} locale Language - * @returns {String} Time + * Formats a time value relative to the current time. + * + * @param {Object} client - The client object containing language data. + * @param {string|number|Date} time - The time value to format. + * @param {boolean} [type=false] - If true, formats the time as "X time ago", otherwise formats it as "in X time". + * @param {boolean} [prefix=true] - If true, includes a prefix like "in" or "ago" in the formatted time. + * @param {string} [locale=client.defaultLanguage.name] - The locale to use for formatting the time. + * @returns {string} The formatted time value. */ convertTime(client, time, type = false, prefix = true, locale = client.defaultLanguage.name) { const languageData = client.languages.find(language => language.name === locale); @@ -107,12 +106,13 @@ module.exports = { }, /** - * Get a Noun for Number - * @param {Number} number Number - * @param {String} one String for one - * @param {String} two String for two - * @param {String} five String for five - * @returns + * Generates the appropriate noun form based on the given number and noun forms. + * + * @param {number} number - The number to use for determining the noun form. + * @param {string} one - The noun form for the singular case. + * @param {string} two - The noun form for the dual case. + * @param {string} five - The noun form for the plural case. + * @returns {string} The appropriate noun form based on the given number. */ getNoun(number, one, two, five) { let n = Math.abs(number); diff --git a/languages/en-US/beatrun.ru/courses.json b/languages/en-US/beatrun.ru/courses.json new file mode 100644 index 00000000..9f94cc08 --- /dev/null +++ b/languages/en-US/beatrun.ru/courses.json @@ -0,0 +1,11 @@ +{ + "DESCRIPTION": "Get course info", + "USAGE": "", + "EXAMPLES": "courses code:ABCD-XZYX-1234", + "NOT_FOUND": "Course not found", + "MAP": "Map", + "UPLOADER": "Uploaded by", + "DATE": "Upload date", + "PLAYS": "Plays", + "DOWNLOAD": "Download" +} \ No newline at end of file diff --git a/languages/en-US/common.json b/languages/en-US/common.json index 4b6052a0..382df13f 100644 --- a/languages/en-US/common.json +++ b/languages/en-US/common.json @@ -9,6 +9,7 @@ "CANCEL": "Cancel", "CHANNEL": "Channel", "CHANNELS": "Channels", + "CODE": "Code", "COLOR": "Color", "CONTENT": "Content", "COMMAND": "Command", diff --git a/languages/ru-RU/beatrun.ru/courses.json b/languages/ru-RU/beatrun.ru/courses.json new file mode 100644 index 00000000..d1913c66 --- /dev/null +++ b/languages/ru-RU/beatrun.ru/courses.json @@ -0,0 +1,11 @@ +{ + "DESCRIPTION": "Получить информацию о курсе", + "USAGE": "", + "EXAMPLES": "courses code:ABCD-XZYX-1234", + "NOT_FOUND": "Курс не найден", + "MAP": "Карта", + "UPLOADER": "Загрузил", + "DATE": "Дата загрузки", + "PLAYS": "Сыграно", + "DOWNLOAD": "Скачать" +} \ No newline at end of file diff --git a/languages/ru-RU/common.json b/languages/ru-RU/common.json index 707c223c..f4deceb3 100644 --- a/languages/ru-RU/common.json +++ b/languages/ru-RU/common.json @@ -9,6 +9,7 @@ "CANCEL": "Отменить", "CHANNEL": "Канал", "CHANNELS": "Каналы", + "CODE": "Код", "COLOR": "Цвет", "CONTENT": "Содержимое", "COMMAND": "Команда", diff --git a/languages/uk-UA/beatrun.ru/courses.json b/languages/uk-UA/beatrun.ru/courses.json new file mode 100644 index 00000000..50e5ab08 --- /dev/null +++ b/languages/uk-UA/beatrun.ru/courses.json @@ -0,0 +1,11 @@ +{ + "DESCRIPTION": "Отримати інформацію про курс", + "USAGE": "", + "EXAMPLES": "courses code:ABCD-XZYX-1234", + "NOT_FOUND": "Курс не знайдено", + "MAP": "Карта", + "UPLOADER": "Завантажив", + "DATE": "Дата завантаження", + "PLAYS": "Зіграно", + "DOWNLOAD": "Завантажити" +} \ No newline at end of file diff --git a/languages/uk-UA/common.json b/languages/uk-UA/common.json index 900eb444..8f8131fb 100644 --- a/languages/uk-UA/common.json +++ b/languages/uk-UA/common.json @@ -9,6 +9,7 @@ "CANCEL": "Скасувати", "CHANNEL": "Канал", "CHANNELS": "Канали", + "CODE": "Код", "COLOR": "Колір", "CONTENT": "Вміст", "COMMAND": "Команда", diff --git a/package.json b/package.json index cd923222..a32bdb15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jaba", - "version": "4.4.4", + "version": "4.5.0", "description": "My Discord Bot", "main": "index.js", "scripts": {