JaBa/base/JaBa.js

396 lines
13 KiB
JavaScript
Raw Normal View History

const { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder } = require("discord.js"),
{ Player } = require("discord-player"),
2022-01-04 02:18:28 +05:00
{ GiveawaysManager } = require("discord-giveaways"),
{ REST } = require("@discordjs/rest"),
{ Routes } = require("discord-api-types/v10");
2022-01-04 02:18:28 +05:00
const BaseEvent = require("./BaseEvent.js"),
BaseCommand = require("./BaseCommand.js"),
2022-01-04 02:18:28 +05:00
path = require("path"),
fs = require("fs").promises,
mongoose = require("mongoose"),
2022-01-04 02:18:28 +05:00
moment = require("moment");
moment.relativeTimeThreshold("ss", 5);
moment.relativeTimeThreshold("s", 60);
2022-01-04 02:18:28 +05:00
moment.relativeTimeThreshold("m", 60);
moment.relativeTimeThreshold("h", 60);
moment.relativeTimeThreshold("d", 24);
moment.relativeTimeThreshold("M", 12);
class JaBa extends Client {
constructor(options) {
super(options);
this.config = require("../config");
this.customEmojis = require("../emojis");
this.languages = require("../languages/language-meta");
this.commands = new Collection();
this.logger = require("../helpers/logger");
this.wait = require("node:timers/promises").setTimeout;
this.functions = require("../helpers/functions");
this.guildsData = require("../base/Guild");
this.usersData = require("../base/User");
this.membersData = require("../base/Member");
this.dashboard = require("../dashboard/app");
this.states = {};
2022-01-04 02:18:28 +05:00
this.knownGuilds = [];
this.databaseCache = {};
this.databaseCache.users = new Collection();
this.databaseCache.guilds = new Collection();
this.databaseCache.members = new Collection();
2022-08-29 21:31:36 +05:00
this.databaseCache.usersReminds = new Collection();
this.databaseCache.mutedUsers = new Collection();
this.player = Player.singleton(this);
this.player.events.on("playerStart", async (queue, track) => {
const m = await queue.metadata.channel.send({ content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language) });
if (track.durationMS > 1)
setTimeout(() => {
if (m.deletable) m.delete();
}, track.durationMS);
else
setTimeout(() => {
if (m.deletable) m.delete();
}, (5 * 60 * 1000)); // m * s * ms
});
this.player.events.on("emptyQueue", queue => queue.metadata.channel.send(this.translate("music/play:QUEUE_ENDED", null, queue.metadata.channel.guild.data.language)));
this.player.events.on("emptyChannel", queue => queue.metadata.channel.send(this.translate("music/play:STOP_EMPTY", null, queue.metadata.channel.guild.data.language)));
this.player.events.on("playerError", (queue, e) => {
console.log(e);
queue.metadata.channel.send({ content: this.translate("music/play:ERR_OCCURRED", { error: e.message }, queue.metadata.channel.guild.data.language) });
});
this.player.events.on("error", (queue, e) => {
console.log(e);
queue.metadata.channel.send({ content: this.translate("music/play:ERR_OCCURRED", { error: e.message }, queue.metadata.channel.guild.data.language) });
});
2022-01-04 02:18:28 +05:00
this.giveawaysManager = new GiveawaysManager(this, {
storage: "./giveaways.json",
default: {
botsCanWin: false,
embedColor: this.config.embed.color,
embedColorEnd: "#FF0000",
reaction: "🎉",
},
2022-01-04 02:18:28 +05:00
});
2022-01-13 00:26:23 +05:00
}
2022-01-04 02:18:28 +05:00
/**
* Login into bot account, connect to DB and update docs
*/
async init() {
this.login(this.config.token);
mongoose.connect(this.config.mongoDB, {
useNewUrlParser: true,
useUnifiedTopology: true,
}).then(() => {
this.logger.log("Connected to the Mongodb database.", "log");
2023-02-01 00:23:01 +05:00
}).catch(err => {
this.logger.log(`Unable to connect to the Mongodb database. Error: ${err}`, "error");
});
const autoUpdateDocs = require("../helpers/autoUpdateDocs");
autoUpdateDocs.update(this);
}
/**
* Load commands from directory
* @param {String} dir Directory where's all commands located
* @returns
*/
async loadCommands(dir) {
2022-12-17 13:49:03 +05:00
const rest = new REST().setToken(this.config.token),
filePath = path.join(__dirname, dir),
folders = (await fs.readdir(filePath))
.map(file => path.join(filePath, file))
.filter(async path => (await fs.lstat(path)).isDirectory());
const commands = [];
for (let index = 0; index < folders.length; index++) {
const folder = folders[index];
const files = await fs.readdir(folder);
for (let index = 0; index < files.length; index++) {
const file = files[index];
if (file.endsWith(".js")) {
const Command = require(path.join(folder, file));
if (Command.prototype instanceof BaseCommand) {
const command = new Command(this);
this.commands.set(command.command.name, command);
const aliases = [];
if (command.aliases && Array.isArray(command.aliases) && command.aliases.length > 0)
2023-02-01 00:23:01 +05:00
command.aliases.forEach(alias => {
const command_alias = (command.command instanceof SlashCommandBuilder || command.command instanceof ContextMenuCommandBuilder) ? { ...command.command.toJSON() } : { ...command.command };
command_alias.name = alias;
aliases.push(command_alias);
this.commands.set(alias, command);
});
commands.push((command.command instanceof SlashCommandBuilder || command.command instanceof ContextMenuCommandBuilder) ? command.command.toJSON() : command.command, ...aliases);
if (command.onLoad || typeof command.onLoad === "function") await command.onLoad(this);
this.logger.log(`Successfully loaded "${file}" command file. (Command: ${command.command.name})`);
}
}
}
}
2022-01-04 02:18:28 +05:00
try {
if (this.config.production)
await rest.put(
2022-10-09 17:57:55 +05:00
Routes.applicationCommands(this.config.user), {
body: commands,
},
);
else
await rest.put(
2022-10-09 17:57:55 +05:00
Routes.applicationGuildCommands(this.config.user, this.config.support.id), {
body: commands,
},
);
this.logger.log("Successfully registered application commands.");
} catch (err) {
this.logger.log("Cannot load commands: " + err.message, "error");
}
}
/**
* Load single command in directory
* @param {String} dir Directory where command is
* @param {String} file Filename of the command
*/
async loadCommand(dir, file) {
const Command = require(path.join(dir, `${file}.js`));
if (Command.prototype instanceof BaseCommand) {
const command = new Command(this);
this.commands.set(command.command.name, command);
const aliases = [];
if (command.aliases && Array.isArray(command.aliases) && command.aliases.length > 0)
2023-02-01 00:23:01 +05:00
command.aliases.forEach(alias => {
const command_alias = command.command instanceof SlashCommandBuilder ? { ...command.command.toJSON() } : { ...command.command };
command_alias.name = alias;
aliases.push(command_alias);
this.commands.set(alias, command);
});
if (command.onLoad || typeof command.onLoad === "function") await command.onLoad(this);
this.logger.log(`Successfully loaded "${file}" command file. (Command: ${command.command.name})`);
return;
}
}
/**
* Unload command from cache
* @param {String} dir Directory of the command
* @param {String} name Name of the command
*/
async unloadCommand(dir, name) {
delete require.cache[require.resolve(`${dir}${path.sep}${name}.js`)];
return;
}
/**
* Load events from directory
* @param {String} dir Directory where's all events located
* @returns
*/
async loadEvents(dir) {
const filePath = path.join(__dirname, dir);
const files = await fs.readdir(filePath);
for (let index = 0; index < files.length; index++) {
const file = files[index];
const stat = await fs.lstat(path.join(filePath, file));
if (stat.isDirectory()) this.loadEvents(path.join(dir, file));
if (file.endsWith(".js")) {
const Event = require(path.join(filePath, file));
if (Event.prototype instanceof BaseEvent) {
const event = new Event();
if (!event.name || !event.name.length) return console.error(`Cannot load "${file}" event file: Event name is not set!`);
if (event.once) this.once(event.name, event.execute.bind(event, this));
else this.on(event.name, event.execute.bind(event, this));
this.logger.log(`Successfully loaded "${file}" event file. (Event: ${event.name})`);
}
}
2022-01-13 00:26:23 +05:00
}
}
2022-01-04 02:18:28 +05:00
/**
* Get default language
2022-12-17 13:49:03 +05:00
* @returns {String} Default bot's language
*/
get defaultLanguage() {
return this.languages.find(language => language.default).name;
}
/**
* Translate from key to language
* @param {String} key Key
* @param {Array} args Arguments for translation
* @param {String} locale Language
*/
translate(key, args, locale = this.defaultLanguage) {
const language = this.translations.get(locale);
if (!language) throw "Invalid language set in data.";
return language(key, args);
}
/**
2022-12-17 13:49:03 +05:00
* Beautify date
* @param {Date} date Date
* @param {String | null} format Format for moment
* @param {String} locale Language
* @returns {String} Beautified date
*/
printDate(date, format = "", locale = this.defaultLanguage) {
2022-11-15 20:37:03 +05:00
const languageData = this.languages.find(language => language.name === locale);
if (format === "" || format === null) format = languageData.defaultMomentFormat;
return moment(new Date(date))
.locale(languageData.moment)
.format(format);
}
/**
* Convert given time
* @param {String} time Time
* @param {Boolean} type Type (To now = true or from now = false)
* @param {Boolean} noPrefix Use prefix?
* @param {String} locale Language
* @returns {String} Time
*/
convertTime(time, type = false, noPrefix = false, locale = this.defaultLanguage) {
2022-11-15 20:37:03 +05:00
const languageData = this.languages.find(language => language.name === locale);
const m = moment(time).locale(languageData.moment);
return (type ? m.toNow(noPrefix) : m.fromNow(noPrefix));
}
/**
* Get 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
*/
getNoun(number, one, two, five) {
let n = Math.abs(number);
n %= 100;
if (n >= 5 && n <= 20) return five;
n %= 10;
if (n === 1) return one;
if (n >= 2 && n <= 4) return two;
return five;
}
/**
* Find or create user in DB
* @param {Array} param0 { id: User ID }
* @param {Boolean} isLean Return JSON instead Mongoose model?
* @returns {import("./User")} Mongoose model or JSON of this user
*/
2022-01-04 02:18:28 +05:00
async findOrCreateUser({ id: userID }, isLean) {
if (this.databaseCache.users.get(userID)) return isLean ? this.databaseCache.users.get(userID).toJSON() : this.databaseCache.users.get(userID);
else {
let userData = (isLean ? await this.usersData.findOne({
id: userID,
2022-01-04 02:18:28 +05:00
}).lean() : await this.usersData.findOne({
id: userID,
2022-01-04 02:18:28 +05:00
}));
if (userData) {
if (!isLean) this.databaseCache.users.set(userID, userData);
return userData;
} else {
userData = new this.usersData({
id: userID,
2022-01-04 02:18:28 +05:00
});
await userData.save();
this.databaseCache.users.set(userID, userData);
return isLean ? userData.toJSON() : userData;
2022-01-13 00:26:23 +05:00
}
}
}
2022-01-04 02:18:28 +05:00
/**
* Find or create member in DB
* @param {Array} param0 { id: Member ID }
* @param {Boolean} isLean Return JSON instead Mongoose model?
* @returns {import("./Member")} Mongoose model or JSON of this member
*/
async findOrCreateMember({ id: memberID, guildId }, isLean) {
if (this.databaseCache.members.get(`${memberID}${guildId}`)) return isLean ? this.databaseCache.members.get(`${memberID}${guildId}`).toJSON() : this.databaseCache.members.get(`${memberID}${guildId}`);
2022-01-04 02:18:28 +05:00
else {
let memberData = (isLean ? await this.membersData.findOne({
guildID: guildId,
id: memberID,
2022-01-04 02:18:28 +05:00
}).lean() : await this.membersData.findOne({
guildID: guildId,
id: memberID,
2022-01-04 02:18:28 +05:00
}));
if (memberData) {
if (!isLean) this.databaseCache.members.set(`${memberID}${guildId}`, memberData);
2022-01-04 02:18:28 +05:00
return memberData;
} else {
memberData = new this.membersData({
id: memberID,
guildID: guildId,
2022-01-04 02:18:28 +05:00
});
await memberData.save();
const guild = await this.findOrCreateGuild({
id: guildId,
2022-01-04 02:18:28 +05:00
});
if (guild) {
guild.members.push(memberData._id);
await guild.save();
2022-01-13 00:26:23 +05:00
}
this.databaseCache.members.set(`${memberID}${guildId}`, memberData);
2022-01-04 02:18:28 +05:00
return isLean ? memberData.toJSON() : memberData;
2022-01-13 00:26:23 +05:00
}
}
}
2022-01-04 02:18:28 +05:00
/**
* Find or create guild in DB
* @param {Array} param0 { id: Guild ID }
* @param {Boolean} isLean Return JSON instead Mongoose model?
* @returns {import("./Guild")} Mongoose model or JSON of this guild
*/
async findOrCreateGuild({ id: guildId }, isLean) {
if (this.databaseCache.guilds.get(guildId)) return isLean ? this.databaseCache.guilds.get(guildId).toJSON() : this.databaseCache.guilds.get(guildId);
2022-01-04 02:18:28 +05:00
else {
let guildData = (isLean ? await this.guildsData.findOne({
id: guildId,
2022-01-04 02:18:28 +05:00
}).populate("members").lean() : await this.guildsData.findOne({
id: guildId,
2022-01-04 02:18:28 +05:00
}).populate("members"));
if (guildData) {
if (!isLean) this.databaseCache.guilds.set(guildId, guildData);
2022-01-04 02:18:28 +05:00
return guildData;
} else {
guildData = new this.guildsData({
id: guildId,
2022-01-04 02:18:28 +05:00
});
await guildData.save();
this.databaseCache.guilds.set(guildId, guildData);
2022-01-04 02:18:28 +05:00
return isLean ? guildData.toJSON() : guildData;
2022-01-13 00:26:23 +05:00
}
}
}
}
2022-01-04 02:18:28 +05:00
module.exports = JaBa;