Compare commits

...

5 commits

Author SHA1 Message Date
b0cdd9b2c9
update ignores 2024-04-07 16:18:13 +05:00
5a7a22c558
push clips folder 2024-04-07 16:18:02 +05:00
3896ee27da
update cat and dog locals 2024-04-07 16:04:47 +05:00
7618492300
update JaBa image 2024-04-07 15:33:25 +05:00
b1d1c5a0ab
v4.5.0 2024-04-07 15:29:16 +05:00
22 changed files with 237 additions and 100 deletions

4
.gitignore vendored
View file

@ -9,10 +9,10 @@ Thumbs.db
/giveaways.json /giveaways.json
# Clips # Clips
/clips /clips/*
# Node # Node
node_modules node_modules
# SQlite # Dashboard DB
/json.sqlite /json.sqlite

View file

@ -1,4 +1,4 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" src="https://cdn.discordapp.com/attachments/1005529755296206940/1090005990717534269/af98d49ebc9bf28b40b45ed5a0a623b4.webp"> <img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" src="https://images-ext-1.discordapp.net/external/khumNb7SPgoX2KAmnB5-37LF8Hsg_gb9BithY5gaO_w/%3Fsize%3D2048/https/cdn.discordapp.com/avatars/708637495054565426/e1e9a50ec08988d1b25c13f8bd4801bd.webp">
# JaBa Bot # JaBa Bot

View file

@ -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<void>} A Promise that resolves when the client is fully initialized.
*/ */
async init() { async init() {
this.login(this.config.token); this.login(this.config.token);
@ -100,8 +104,14 @@ class JaBaClient extends Client {
} }
/** /**
* Loads all commands from directory * Loads all the commands from the specified directory and registers them with the Discord API.
* @param {String} dir Directory where commands are located *
* 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<void>} A Promise that resolves when all the commands have been loaded and registered.
*/ */
async loadCommands(dir) { async loadCommands(dir) {
const rest = new REST().setToken(this.config.token), 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() { get defaultLanguage() {
return this.languages.find(language => language.default); return this.languages.find(language => language.default);
} }
/** /**
* Translates from a key to language * Translates a key using the specified locale, or the default language if no locale is provided.
* @param {String} key Key * @param {string} key The translation key to look up.
* @param {Array} args Arguments for translation * @param {any[]} args Any arguments to pass to the translation function.
* @param {String} locale Language * @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) { translate(key, args, locale = this.defaultLanguage.name) {
const lang = this.translations.get(locale); const lang = this.translations.get(locale);
@ -166,9 +178,19 @@ class JaBaClient extends Client {
} }
/** /**
* Returns an embed created from given data * Generates an EmbedBuilder instance with the provided data.
* @param {Object} data Data for embed * @param {Object} data - The data to use for the embed.
* @returns {import("discord.js").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) { embed(data) {
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
@ -193,16 +215,16 @@ class JaBaClient extends Client {
if (!data.author || data.author === null) embed.setAuthor(null); 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 === "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); else embed.setAuthor(data.author);
return embed; return embed;
} }
/** /**
* Creates an invite link for guild * Creates an invite for the specified guild.
* @param {String} guildId Guild ID * @param {string} guildId - The ID of the guild to create the invite for.
* @returns {String} Invite link * @returns {Promise<string>} 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) { async createInvite(guildId) {
const guild = this.guilds.cache.get(guildId), const guild = this.guilds.cache.get(guildId),
@ -213,9 +235,10 @@ class JaBaClient extends Client {
} }
/** /**
* Loads a single command from directory * Loads a command from the specified directory and file.
* @param {String} dir Directory where command is located * @param {string} dir - The directory containing the command file.
* @param {String} file Filename of the command * @param {string} file - The name of the command file (without the .js extension).
* @returns {Promise<string>} A log message indicating the successful loading of the command.
*/ */
async loadCommand(dir, file) { async loadCommand(dir, file) {
const Command = require(path.join(dir, `${file}.js`)); const Command = require(path.join(dir, `${file}.js`));
@ -230,10 +253,10 @@ class JaBaClient extends Client {
} }
/** /**
* Removes a command from cache * Unloads a command from the specified directory and file.
* @param {String} dir Directory where command is located * @param {string} dir - The directory containing the command file.
* @param {String} name Command name * @param {string} name - The name of the command file (without the .js extension).
* @returns * @returns {void} This method does not return a value.
*/ */
async unloadCommand(dir, name) { async unloadCommand(dir, name) {
delete require.cache[require.resolve(`${dir}${path.sep}${name}.js`)]; delete require.cache[require.resolve(`${dir}${path.sep}${name}.js`)];
@ -242,8 +265,9 @@ class JaBaClient extends Client {
} }
/** /**
* Loads all events from directory recursively * Loads all event files from the specified directory and its subdirectories.
* @param {String} dir Directory where events are located * @param {string} dir - The directory containing the event files.
* @returns {void} This method does not return a value.
*/ */
async loadEvents(dir) { async loadEvents(dir) {
const filePath = path.join(__dirname, dir); const filePath = path.join(__dirname, dir);
@ -270,9 +294,9 @@ class JaBaClient extends Client {
} }
/** /**
* Finds or creates a user in the database * Finds or creates a user in the database based on the provided user ID.
* @param {String} userID User ID * @param {string} userID - The ID of the user to find or create.
* @returns {Promise<import("./User")>} Mongoose model * @returns {Promise<Object>} The user data object, either retrieved from the database or newly created.
*/ */
async findOrCreateUser(userID) { async findOrCreateUser(userID) {
let userData = await this.usersData.findOne({ id: 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 * Finds or creates a member in the database based on the provided member ID and guild ID.
* @param {Array} { id: Member ID, Guild ID } * @param {Object} options - The options for finding or creating the member.
* @returns {Promise<import("./Member")>} Mongoose model * @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<Object>} The member data object, either retrieved from the database or newly created.
*/ */
async findOrCreateMember({ id: memberID, guildId }) { async findOrCreateMember({ id: memberID, guildId }) {
let memberData = await this.membersData.findOne({ guildID: guildId, id: memberID }); let memberData = await this.membersData.findOne({ guildID: guildId, id: memberID });
@ -324,9 +350,9 @@ class JaBaClient extends Client {
} }
/** /**
* Finds or creates guild in DB * Finds or creates a guild in the database based on the provided guild ID.
* @param {String} guildId Guild ID * @param {string} guildId - The ID of the guild to find or create.
* @returns {Promise<import("./Guild")>} Mongoose model * @returns {Promise<Object>} The guild data object, either retrieved from the database or newly created.
*/ */
async findOrCreateGuild(guildId) { async findOrCreateGuild(guildId) {
let guildData = await this.guildsData.findOne({ id: guildId }).populate("members"); let guildData = await this.guildsData.findOne({ id: guildId }).populate("members");

0
clips/.gitkeep Normal file
View file

View file

@ -91,11 +91,11 @@ class Poll extends BaseCommand {
author: interaction.translate("moderation/poll:TITLE"), author: interaction.translate("moderation/poll:TITLE"),
fields: [ fields: [
{ {
name: "\u200b", name: "\u200B",
value: question, value: question,
}, },
{ {
name: "\u200b", name: "\u200B",
value: interaction.translate("moderation/poll:REACT", { value: interaction.translate("moderation/poll:REACT", {
success: cool.toString(), success: cool.toString(),
error: notcool.toString(), error: notcool.toString(),

View file

@ -278,7 +278,7 @@ async function updateEmbed(interaction, queue) {
value: progressBar, value: progressBar,
}, },
{ {
name: "\u200b", name: "\u200B",
value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[mode]}\``, value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[mode]}\``,
}, },
], ],

View file

@ -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 || response.course.uploader.userid}](https://steamcommunity.com/profiles/${response.course.uploader.userid})`,
inline: true,
},
{
name: "\u200B",
value: "\u200B",
inline: true,
},
{
name: interaction.translate("beatrun.ru/courses:DATE"),
value: `<t:${Math.floor(response.course.time / 1000)}:D>`,
inline: true,
},
{
name: interaction.translate("beatrun.ru/courses:PLAYS"),
value: `${response.course.plays || 0}`,
inline: true,
},
],
});
interaction.editReply({
embeds: [embed],
});
}
}
module.exports = Courses;

View file

@ -1,18 +1,12 @@
const moment = require("moment"); 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 = { module.exports = {
/** /**
* Calls a Callback for Each Element in Collection Asynchronously * Asynchronously iterates over a collection and executes a callback function for each item.
* @param {Array} collection Collection *
* @param {Function} callback Function to Perform on Collection * @param {any[]} collection - The collection to iterate over.
* @returns {Promise} * @param {(item: any) => Promise<void>} callback - The async callback function to execute for each item in the collection.
* @returns {Promise<void>} A promise that resolves when all items in the collection have been processed.
*/ */
async asyncForEach(collection, callback) { async asyncForEach(collection, callback) {
const allPromises = collection.map(async key => { const allPromises = collection.map(async key => {
@ -23,10 +17,11 @@ module.exports = {
}, },
/** /**
* Sorts an Array by Key * Sorts an array by the specified key in ascending order.
* @param {Array} array Array to Sort *
* @param {Number} key Key * @param {any[]} array - The array to sort.
* @returns {Array} Sorted Array * @param {string} key - The key to sort the array by.
* @returns {any[]} The sorted array.
*/ */
sortByKey(array, key) { sortByKey(array, key) {
return array.sort(function (a, b) { return array.sort(function (a, b) {
@ -37,9 +32,10 @@ module.exports = {
}, },
/** /**
* Shuffles the Array * Shuffles the elements of the provided array in-place.
* @param {Array} pArray Array to Shuffle *
* @returns {Array} Shuffled Array * @param {any[]} pArray - The array to shuffle.
* @returns {any[]} The shuffled array.
*/ */
shuffle(pArray) { shuffle(pArray) {
const array = []; const array = [];
@ -63,10 +59,11 @@ module.exports = {
}, },
/** /**
* Returns a Random Number Between min (inclusive) And max (inclusive) * Generates a random integer between the specified minimum and maximum values (inclusive).
* @param {Number} min Min value (inclusive) *
* @param {Number} max Max value (inclusive) * @param {number} min - The minimum value (inclusive).
* @returns {Number} Integer * @param {number} max - The maximum value (inclusive).
* @returns {number} A random integer between min and max.
*/ */
randomNum(min, max) { randomNum(min, max) {
min = Math.ceil(min); min = Math.ceil(min);
@ -76,12 +73,13 @@ module.exports = {
}, },
/** /**
* Beautifies the given Date * Formats a date for the specified client and locale.
* @param {import("../base/Client")} client Discord Client *
* @param {Date} date Date * @param {Object} client - The client object containing language data.
* @param {String | null} format Format for Moment * @param {string} date - The date to format.
* @param {String} locale Language * @param {string} [format=null] - The date format to use. If not provided, the default format for the client's language will be used.
* @returns {String} Beautified Date * @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) { printDate(client, date, format = null, locale = client.defaultLanguage.name) {
const languageData = client.languages.find(language => language.name === locale); const languageData = client.languages.find(language => language.name === locale);
@ -91,13 +89,14 @@ module.exports = {
}, },
/** /**
* Converts the Given Time * Formats a time value relative to the current time.
* @param {import("../base/Client")} client Discord Client *
* @param {String} time Time * @param {Object} client - The client object containing language data.
* @param {Boolean} type Type (To now = true or from now = false) * @param {string|number|Date} time - The time value to format.
* @param {Boolean} prefix Include Prefix * @param {boolean} [type=false] - If true, formats the time as "X time ago", otherwise formats it as "in X time".
* @param {String} locale Language * @param {boolean} [prefix=true] - If true, includes a prefix like "in" or "ago" in the formatted time.
* @returns {String} 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) { convertTime(client, time, type = false, prefix = true, locale = client.defaultLanguage.name) {
const languageData = client.languages.find(language => language.name === locale); const languageData = client.languages.find(language => language.name === locale);
@ -107,12 +106,13 @@ module.exports = {
}, },
/** /**
* Get a Noun for Number * Generates the appropriate noun form based on the given number and noun forms.
* @param {Number} number Number *
* @param {String} one String for one * @param {number} number - The number to use for determining the noun form.
* @param {String} two String for two * @param {string} one - The noun form for the singular case.
* @param {String} five String for five * @param {string} two - The noun form for the dual case.
* @returns * @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) { getNoun(number, one, two, five) {
let n = Math.abs(number); let n = Math.abs(number);

View file

@ -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"
}

View file

@ -9,6 +9,7 @@
"CANCEL": "Cancel", "CANCEL": "Cancel",
"CHANNEL": "Channel", "CHANNEL": "Channel",
"CHANNELS": "Channels", "CHANNELS": "Channels",
"CODE": "Code",
"COLOR": "Color", "COLOR": "Color",
"CONTENT": "Content", "CONTENT": "Content",
"COMMAND": "Command", "COMMAND": "Command",

View file

@ -1,5 +1,5 @@
{ {
"DESCRIPTION": "Everything is like with a crab, but with a cat!", "DESCRIPTION": "Sends an image of a cat... impossible!",
"USAGE": "", "USAGE": "",
"EXAMPLES": "cat" "EXAMPLES": "cat"
} }

View file

@ -1,5 +0,0 @@
{
"DESCRIPTION": "Sends an image of a crab... that's it",
"USAGE": "",
"EXAMPLES": "crab"
}

View file

@ -0,0 +1,11 @@
{
"DESCRIPTION": "Получить информацию о курсе",
"USAGE": "",
"EXAMPLES": "courses code:ABCD-XZYX-1234",
"NOT_FOUND": "Курс не найден",
"MAP": "Карта",
"UPLOADER": "Загрузил",
"DATE": "Дата загрузки",
"PLAYS": "Сыграно",
"DOWNLOAD": "Скачать"
}

View file

@ -9,6 +9,7 @@
"CANCEL": "Отменить", "CANCEL": "Отменить",
"CHANNEL": "Канал", "CHANNEL": "Канал",
"CHANNELS": "Каналы", "CHANNELS": "Каналы",
"CODE": "Код",
"COLOR": "Цвет", "COLOR": "Цвет",
"CONTENT": "Содержимое", "CONTENT": "Содержимое",
"COMMAND": "Команда", "COMMAND": "Команда",

View file

@ -1,5 +1,5 @@
{ {
"DESCRIPTION": "Всё как с крабом, только котик", "DESCRIPTION": "Отправляет фотокарточку котика... невозможно!",
"USAGE": "", "USAGE": "",
"EXAMPLES": "cat" "EXAMPLES": "cat"
} }

View file

@ -1,5 +0,0 @@
{
"DESCRIPTION": "Получить изображение краба... да, это всё",
"USAGE": "",
"EXAMPLES": "crab"
}

View file

@ -0,0 +1,11 @@
{
"DESCRIPTION": "Отримати інформацію про курс",
"USAGE": "",
"EXAMPLES": "courses code:ABCD-XZYX-1234",
"NOT_FOUND": "Курс не знайдено",
"MAP": "Карта",
"UPLOADER": "Завантажив",
"DATE": "Дата завантаження",
"PLAYS": "Зіграно",
"DOWNLOAD": "Завантажити"
}

View file

@ -9,6 +9,7 @@
"CANCEL": "Скасувати", "CANCEL": "Скасувати",
"CHANNEL": "Канал", "CHANNEL": "Канал",
"CHANNELS": "Канали", "CHANNELS": "Канали",
"CODE": "Код",
"COLOR": "Колір", "COLOR": "Колір",
"CONTENT": "Вміст", "CONTENT": "Вміст",
"COMMAND": "Команда", "COMMAND": "Команда",

View file

@ -1,5 +1,5 @@
{ {
"DESCRIPTION": "Всё как с крабом, только котик", "DESCRIPTION": "Надсилає фотокартку котика... неможливо!",
"USAGE": "", "USAGE": "",
"EXAMPLES": "cat" "EXAMPLES": "cat"
} }

View file

@ -1,5 +0,0 @@
{
"DESCRIPTION": "Отримати зображення краба... так, це все",
"USAGE": "",
"EXAMPLES": "crab"
}

View file

@ -1,5 +1,5 @@
{ {
"DESCRIPTION": "Вы не поверите... изображение собачки!", "DESCRIPTION": "Ви не повірите... зображення собачки!",
"USAGE": "", "USAGE": "",
"EXAMPLES": "dog" "EXAMPLES": "dog"
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "jaba", "name": "jaba",
"version": "4.4.4", "version": "4.5.0",
"description": "My Discord Bot", "description": "My Discord Bot",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {