update dev...

This commit is contained in:
JonnyBro 2022-11-09 18:52:12 +05:00
commit 9fe5e01cdb
352 changed files with 1333 additions and 13982 deletions

View file

@ -16,7 +16,6 @@ class Seek extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -14,11 +14,7 @@ class BaseCommand {
/**
* @type {Boolean}
*/
this.guildOnly = options.guildOnly || true;
/**
* @type {Boolean}
*/
this.ownerOnly = options.ownerOnly || false;
this.ownerOnly = (options.ownerOnly === true ? true : false) || false;
this.dirname = options.dirname || false;
/**
* @type {String}

View file

@ -1,5 +1,5 @@
const { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder } = require("discord.js"),
{ Player } = require("../helpers/Music/dist/index"),
{ Player } = require("discord-player-play-dl"),
{ DiscordTogether } = require("../helpers/discordTogether"),
{ GiveawaysManager } = require("discord-giveaways"),
{ REST } = require("@discordjs/rest"),
@ -46,16 +46,14 @@ class JaBa extends Client {
this.discordTogether = new DiscordTogether(this);
playdl.getFreeClientID().then(clientID => {
playdl.setToken({
soundcloud: {
client_id: clientID
}
});
});
this.player = new Player(this);
playdl.getFreeClientID().then(id => playdl.setToken({
soundcloud: {
client_id: id
}
}));
this.player.on("trackStart", 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) {
@ -90,6 +88,9 @@ class JaBa extends Client {
});
}
/**
* Login into bot account, connect to DB and update docs
*/
async init() {
this.login(this.config.token);
@ -107,8 +108,8 @@ class JaBa extends Client {
}
/**
*
* @param {String} dir
* Load commands from directory
* @param {String} dir Directory where's all commands located
* @returns
*/
async loadCommands(dir) {
@ -148,15 +149,15 @@ class JaBa extends Client {
}
try {
if (!this.config.production) {
if (this.config.production) {
await rest.put(
Routes.applicationGuildCommands(this.config.user, this.config.support.id), {
Routes.applicationCommands(this.config.user), {
body: commands
}
);
} else {
await rest.put(
Routes.applicationCommands(this.config.user), {
Routes.applicationGuildCommands(this.config.user, this.config.support.id), {
body: commands
}
);
@ -169,9 +170,9 @@ class JaBa extends Client {
}
/**
*
* @param {String} dir
* @param {String} file
* 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`));
@ -196,9 +197,9 @@ class JaBa extends Client {
}
/**
*
* @param {String} dir
* @param {String} name
* 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`)];
@ -207,8 +208,8 @@ class JaBa extends Client {
}
/**
*
* @param {String} dir
* Load events from directory
* @param {String} dir Directory where's all events located
* @returns
*/
async loadEvents(dir) {
@ -231,15 +232,18 @@ class JaBa extends Client {
}
}
/**
* Get default language
*/
get defaultLanguage() {
return this.languages.find(language => language.default).name;
}
/**
*
* @param {String} key
* @param {Array} args
* @param {String} locale
* 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);
@ -248,22 +252,45 @@ class JaBa extends Client {
return language(key, args);
}
printDate(date, format = false, locale = this.defaultLanguage) {
const languageData = this.languages.find((language) => language.name === locale || language.aliases.includes(locale));
if (!format) format = languageData.defaultMomentFormat;
/**
* Returns beautified 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) {
const languageData = this.languages.find(language => language.name === locale || language.aliases.includes(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) {
const languageData = this.languages.find((language) => language.name === locale || language.aliases.includes(locale));
const languageData = this.languages.find(language => language.name === locale || language.aliases.includes(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;
@ -275,6 +302,12 @@ class JaBa extends Client {
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
*/
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 {
@ -299,58 +332,70 @@ class JaBa extends Client {
}
}
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}`);
/**
* 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}`);
else {
let memberData = (isLean ? await this.membersData.findOne({
guildID,
guildID: guildId,
id: memberID
}).lean() : await this.membersData.findOne({
guildID,
guildID: guildId,
id: memberID
}));
if (memberData) {
if (!isLean) this.databaseCache.members.set(`${memberID}${guildID}`, memberData);
if (!isLean) this.databaseCache.members.set(`${memberID}${guildId}`, memberData);
return memberData;
} else {
memberData = new this.membersData({
id: memberID,
guildID: guildID
guildID: guildId
});
await memberData.save();
const guild = await this.findOrCreateGuild({
id: guildID
id: guildId
});
if (guild) {
guild.members.push(memberData._id);
await guild.save();
}
this.databaseCache.members.set(`${memberID}${guildID}`, memberData);
this.databaseCache.members.set(`${memberID}${guildId}`, memberData);
return isLean ? memberData.toJSON() : memberData;
}
}
}
async findOrCreateGuild({ id: guildID }, isLean) {
if (this.databaseCache.guilds.get(guildID)) return isLean ? this.databaseCache.guilds.get(guildID).toJSON() : this.databaseCache.guilds.get(guildID);
/**
* 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);
else {
let guildData = (isLean ? await this.guildsData.findOne({
id: guildID
id: guildId
}).populate("members").lean() : await this.guildsData.findOne({
id: guildID
id: guildId
}).populate("members"));
if (guildData) {
if (!isLean) this.databaseCache.guilds.set(guildID, guildData);
if (!isLean) this.databaseCache.guilds.set(guildId, guildData);
return guildData;
} else {
guildData = new this.guildsData({
id: guildID
id: guildId
});
await guildData.save();
this.databaseCache.guilds.set(guildID, guildData);
this.databaseCache.guilds.set(guildId, guildData);
return isLean ? guildData.toJSON() : guildData;
}

View file

@ -11,6 +11,7 @@ class Automod extends BaseCommand {
command: new SlashCommandBuilder()
.setName("automod")
.setDescription(client.translate("administration/automod:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Automod extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Autorole extends BaseCommand {
command: new SlashCommandBuilder()
.setName("autorole")
.setDescription(client.translate("administration/autorole:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -19,7 +20,7 @@ class Autorole extends BaseCommand {
.setDescription(client.translate("common:ROLE"))),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,10 +11,11 @@ class Config extends BaseCommand {
command: new SlashCommandBuilder()
.setName("config")
.setDescription(client.translate("administration/config:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Goodbye extends BaseCommand {
command: new SlashCommandBuilder()
.setName("goodbye")
.setDescription(client.translate("administration/goodbye:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addSubcommand(subcommand => subcommand.setName("test")
.setDescription(client.translate("administration/goodbye:TEST"))
@ -29,7 +30,7 @@ class Goodbye extends BaseCommand {
),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Set extends BaseCommand {
command: new SlashCommandBuilder()
.setName("set")
.setDescription(client.translate("administration/set:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addStringOption(option => option.setName("type")
.setDescription(client.translate("owner/debug:TYPE"))
@ -29,7 +30,6 @@ class Set extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -50,8 +50,10 @@ class Set extends BaseCommand {
const type = interaction.options.getString("type");
const member = interaction.options.getMember("user");
if (member.user.bot) return interaction.error("misc:BOT_USER", null, { ephemeral: true });
const memberData = await client.findOrCreateMember({
id: member.id
id: member.id,
guildId: interaction.guildId
});
const int = interaction.options.getInteger("int");
if (int < 0) return interaction.error("administration/set:INVALID_NUMBER", null, { ephemeral: true });

View file

@ -11,6 +11,7 @@ class Setbirthdays extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setbirthdays")
.setDescription(client.translate("administration/setbirthdays:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Setbirthdays extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Setlang extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setlang")
.setDescription(client.translate("administration/setlang:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addStringOption(option => option.setName("language")
.setDescription(client.translate("common:LANGUAGE"))
@ -21,7 +22,7 @@ class Setlang extends BaseCommand {
)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Setmodlogs extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setmodlogs")
.setDescription(client.translate("administration/setmodlogs:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Setmodlogs extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Setnews extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setnews")
.setDescription(client.translate("administration/setnews:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Setnews extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Setreports extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setreports")
.setDescription(client.translate("administration/setreports:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Setreports extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Setsuggests extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setsuggests")
.setDescription(client.translate("administration/setsuggests:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addBooleanOption(option => option.setName("state")
.setDescription(client.translate("common:STATE"))
@ -20,7 +21,7 @@ class Setsuggests extends BaseCommand {
.addChannelTypes(ChannelType.GuildText)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,13 +11,14 @@ class Stealemoji extends BaseCommand {
command: new SlashCommandBuilder()
.setName("stealemoji")
.setDescription(client.translate("administration/stealemoji:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addStringOption(option => option.setName("emoji")
.setDescription(client.translate("common:EMOJI"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,6 +11,7 @@ class Welcome extends BaseCommand {
command: new SlashCommandBuilder()
.setName("welcome")
.setDescription(client.translate("administration/welcome:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.addSubcommand(subcommand => subcommand.setName("test")
.setDescription(client.translate("administration/goodbye:TEST"))
@ -29,7 +30,7 @@ class Welcome extends BaseCommand {
),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,11 +11,11 @@ class Achievements extends BaseCommand {
command: new SlashCommandBuilder()
.setName("achievements")
.setDescription(client.translate("economy/achievements:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,6 +11,7 @@ class Bank extends BaseCommand {
command: new SlashCommandBuilder()
.setName("bank")
.setDescription(client.translate("economy/bank:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("option")
.setDescription(client.translate("economy/bank:OPTION"))
.setRequired(true)
@ -23,7 +24,6 @@ class Bank extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -48,19 +48,18 @@ class Bank extends BaseCommand {
if (isNaN(credits) || credits < 1) return interaction.error("misc:OPTION_NAN_ALL");
if (data.memberData.money < credits) return interaction.error("economy/bank:NOT_ENOUGH_CREDIT", { money: `**${credits}** ${client.getNoun(credits, interaction.translate("misc:NOUNS:CREDIT:1"), interaction.translate("misc:NOUNS:CREDIT:2"), interaction.translate("misc:NOUNS:CREDIT:5"))}` });
data.memberData.money -= credits;
data.memberData.bankSold += credits;
await data.memberData.save();
const info = {
user: interaction.translate("economy/transactions:BANK"),
amount: credits,
date: Date.now(),
type: "send"
};
data.memberData.transactions.push(info);
data.memberData.money -= credits;
data.memberData.bankSold += credits;
await data.memberData.save();
interaction.success("economy/bank:SUCCESS_DEP", {
money: `**${credits}** ${client.getNoun(credits, interaction.translate("misc:NOUNS:CREDIT:1"), interaction.translate("misc:NOUNS:CREDIT:2"), interaction.translate("misc:NOUNS:CREDIT:5"))}`
});

View file

@ -11,6 +11,7 @@ class Birthdate extends BaseCommand {
command: new SlashCommandBuilder()
.setName("birthdate")
.setDescription(client.translate("economy/birthdate:DESCRIPTION"))
.setDMPermission(false)
.addIntegerOption(option => option.setName("day")
.setDescription(client.translate("economy/birthdate:DAY"))
.setRequired(true))
@ -36,7 +37,6 @@ class Birthdate extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -10,10 +10,10 @@ class Divorce extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("divorce")
.setDescription(client.translate("economy/divorce:DESCRIPTION")),
.setDescription(client.translate("economy/divorce:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,10 +11,10 @@ class ImportMee6 extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("importmee6")
.setDescription(client.translate("economy/importmee6:DESCRIPTION")),
.setDescription(client.translate("economy/importmee6:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -1,6 +1,14 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
const asyncForEach = async (collection, callback) => {
const allPromises = collection.map(async key => {
await callback(key);
});
return await Promise.all(allPromises);
};
class Leaderboard extends BaseCommand {
/**
*
@ -11,6 +19,7 @@ class Leaderboard extends BaseCommand {
command: new SlashCommandBuilder()
.setName("leaderboard")
.setDescription(client.translate("economy/leaderboard:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("type")
.setDescription(client.translate("owner/debug:TYPE"))
.setRequired(true)
@ -21,7 +30,6 @@ class Leaderboard extends BaseCommand {
)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -40,6 +48,7 @@ class Leaderboard extends BaseCommand {
*/
async execute(client, interaction) {
await interaction.deferReply();
const type = interaction.options.getString("type");
const isOnMobile = JSON.stringify(Object.keys(interaction.member.presence.clientStatus)) === JSON.stringify(["mobile"]);
if (isOnMobile) interaction.followUp({
@ -48,24 +57,24 @@ class Leaderboard extends BaseCommand {
});
if (type === "money") {
const members = await client.membersData.find({
guildID: interaction.guildId
}).lean(),
membersLeaderboard = members.map(m => {
return {
id: m.id,
money: m.money + m.bankSold
};
}).sort((a, b) => b.money - a.money);
const membersLeaderboard = [],
membersData = await client.membersData.find({ guildID: interaction.guildId }).lean();
await asyncForEach(membersData, async member => {
membersLeaderboard.push({
id: member.id,
money: member.money + member.bankSold
});
});
membersLeaderboard.sort((a, b) => b.money - a.money);
if (membersLeaderboard.length > 20) membersLeaderboard.length = 20;
let userNames = "";
let money = "";
for (let i = 0; i < membersLeaderboard.length; i++) {
const data = membersLeaderboard[i];
const user = await client.users.fetch(data.id);
userNames += `**${i + 1}**. ${user}\n`;
userNames += `**${i + 1}**. <@${data.id}>\n`;
money += `${data.money}\n`;
}
@ -78,7 +87,7 @@ class Leaderboard extends BaseCommand {
})
.setColor(client.config.embed.color)
.addFields({
name: interaction.translate("economy/leaderboard:TOP"),
name: interaction.translate("common:USER"),
value: userNames,
inline: true
}, {
@ -91,14 +100,14 @@ class Leaderboard extends BaseCommand {
embeds: [embed]
});
} else if (type === "level") {
const membersLeaderboard = [];
client.membersData.find({
guildID: interaction.guildId
}).lean().then(async m => {
await membersLeaderboard.push({
id: m.id,
level: m.level,
xp: m.exp
const membersLeaderboard = [],
membersData = await client.membersData.find({ guildID: interaction.guildId }).lean();
await asyncForEach(membersData, async member => {
membersLeaderboard.push({
id: member.id,
level: member.level,
xp: member.exp
});
});
membersLeaderboard.sort((a, b) => b.level - a.level);
@ -109,9 +118,8 @@ class Leaderboard extends BaseCommand {
const xp = [];
for (let i = 0; i < membersLeaderboard.length; i++) {
const data = membersLeaderboard[i];
const user = await client.users.fetch(data.id);
userNames.push(`**${i + 1}**. ${user.tag}`);
userNames.push(`**${i + 1}**. <@${data.id}>`);
level.push(`${data.level}`);
xp.push(`${data.xp} / ${5 * (data.level * data.level) + 80 * data.level + 100}`);
}
@ -126,7 +134,7 @@ class Leaderboard extends BaseCommand {
.setColor(client.config.embed.color)
.addFields([
{
name: interaction.translate("economy/leaderboard:TOP"),
name: interaction.translate("common:USER"),
value: userNames.join("\n"),
inline: true
},
@ -146,24 +154,24 @@ class Leaderboard extends BaseCommand {
embeds: [embed]
});
} else if (type === "rep") {
const users = await client.usersData.find({
rep: { $gt: 0 }
}).lean(),
usersLeaderboard = users.map(u => {
return {
id: u.id,
rep: u.rep
};
}).sort((a, b) => b.rep - a.rep);
const usersLeaderboard = [],
usersData = await client.usersData.find({ rep: { $gt: 0 } }).lean();
await asyncForEach(usersData, async user => {
usersLeaderboard.push({
id: user.id,
rep: user.rep
});
});
usersLeaderboard.sort((a, b) => b.rep - a.rep);
if (usersLeaderboard.length > 20) usersLeaderboard.length = 20;
let userNames = "";
let rep = "";
for (let i = 0; i < usersLeaderboard.length; i++) {
const data = usersLeaderboard[i];
const user = await client.users.fetch(data.id);
userNames += `**${i + 1}**. ${user}\n`;
userNames += `**${i + 1}**. <@${data.id}>\n`;
rep += `${data.rep}\n`;
}
@ -176,7 +184,7 @@ class Leaderboard extends BaseCommand {
})
.setColor(client.config.embed.color)
.addFields({
name: interaction.translate("economy/leaderboard:TOP"),
name: interaction.translate("common:USER"),
value: userNames,
inline: true
}, {

View file

@ -12,12 +12,12 @@ class Marry extends BaseCommand {
command: new SlashCommandBuilder()
.setName("marry")
.setDescription(client.translate("economy/marry:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -1,5 +1,6 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
const asyncForEach = async (collection, callback) => {
const allPromises = collection.map(async key => {
await callback(key);
@ -18,11 +19,11 @@ class Money extends BaseCommand {
command: new SlashCommandBuilder()
.setName("money")
.setDescription(client.translate("economy/money:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -44,9 +45,9 @@ class Money extends BaseCommand {
const member = interaction.options.getMember("user") || interaction.member;
if (member.user.bot) return interaction.error("economy/money:BOT_USER");
const memberData = (member.id === interaction.user.id) ? data.memberData : await client.findOrCreateMember({
const memberData = member.id === interaction.user.id ? data.memberData : await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
const guilds = client.guilds.cache.filter(g => g.members.cache.find(m => m.id === member.id));
@ -54,7 +55,7 @@ class Money extends BaseCommand {
await asyncForEach(guilds, async guild => {
const data = await client.findOrCreateMember({
id: member.id,
guildID: guild.id
guildId: guild.id
});
globalMoney += data.money + data.bankSold;
});

View file

@ -11,10 +11,10 @@ class Number extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("number")
.setDescription(client.translate("economy/number:DESCRIPTION")),
.setDescription(client.translate("economy/number:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -79,19 +79,19 @@ class Number extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: msg.author.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
memberData.money += won;
const info = {
user: interaction.translate("economy/transactions:NUMBERS"),
amount: won,
date: Date.now(),
type: "got"
};
data.memberData.transactions.push(info);
memberData.money += won;
await memberData.save();
}
collector.stop();

View file

@ -11,6 +11,7 @@ class Pay extends BaseCommand {
command: new SlashCommandBuilder()
.setName("pay")
.setDescription(client.translate("economy/pay:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true))
@ -19,7 +20,6 @@ class Pay extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -50,23 +50,31 @@ class Pay extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
const info = {
user: member.user.tag,
amount: amount,
date: Date.now(),
type: "send"
};
data.memberData.transactions.push(info);
data.memberData.money -= amount;
await data.memberData.save();
memberData.money += amount;
memberData.save();
const info1 = {
user: member.user.tag,
amount: amount,
date: Date.now(),
type: "send"
};
data.memberData.transactions.push(info1);
const info2 = {
user: member.user.tag,
amount: amount,
date: Date.now(),
type: "got"
};
data.memberData.transactions.push(info2);
interaction.success("economy/pay:SUCCESS", {
user: member.toString(),
amount: `**${amount}** ${client.getNoun(amount, interaction.translate("misc:NOUNS:CREDIT:1"), interaction.translate("misc:NOUNS:CREDIT:2"), interaction.translate("misc:NOUNS:CREDIT:5"))}`

View file

@ -1,5 +1,6 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
const asyncForEach = async (collection, callback) => {
const allPromises = collection.map(async key => {
await callback(key);
@ -18,11 +19,11 @@ class Profile extends BaseCommand {
command: new SlashCommandBuilder()
.setName("profile")
.setDescription(client.translate("economy/profile:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -47,7 +48,7 @@ class Profile extends BaseCommand {
const memberData = (member.id === interaction.user.id ? data.memberData : await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
}));
const userData = (member.id === interaction.user.id ? data.userData : await client.findOrCreateUser({
id: member.id
@ -59,7 +60,7 @@ class Profile extends BaseCommand {
await asyncForEach(guilds, async guild => {
const data = await client.findOrCreateMember({
id: member.id,
guildID: guild.id
guildId: guild.id
});
globalMoney += data.money + data.bankSold;
});

View file

@ -11,12 +11,12 @@ class Rep extends BaseCommand {
command: new SlashCommandBuilder()
.setName("rep")
.setDescription(client.translate("economy/rep:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,6 +11,7 @@ class Rob extends BaseCommand {
command: new SlashCommandBuilder()
.setName("rob")
.setDescription(client.translate("economy/rob:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true))
@ -19,7 +20,6 @@ class Rob extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -45,9 +45,10 @@ class Rob extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
if (amount > memberData.money) return interaction.error("economy/rob:NOT_ENOUGH_MEMBER", { user: member.toString() });
const isInCooldown = memberData.cooldowns.rob || 0;
if (isInCooldown) {
if (isInCooldown > Date.now()) return interaction.error("economy/rob:COOLDOWN", { user: member.toString() });

View file

@ -11,12 +11,12 @@ class Setbio extends BaseCommand {
command: new SlashCommandBuilder()
.setName("setbio")
.setDescription(client.translate("economy/setbio:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("text")
.setDescription(client.translate("economy/profile:BIO"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Slots extends BaseCommand {
command: new SlashCommandBuilder()
.setName("slots")
.setDescription(client.translate("economy/slots:DESCRIPTION"))
.setDMPermission(false)
.addIntegerOption(option => option.setName("amount")
.setDescription(client.translate("common:INT"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -35,8 +35,9 @@ class Slots extends BaseCommand {
*/
async execute(client, interaction, data) {
await interaction.deferReply();
const amount = interaction.options.getInteger("amount");
if (amount > data.memberData.money) return interaction.error("economy/slots:NOT_ENOUGH", { money: `**${amount}** ${client.getNoun(amount, interaction.translate("misc:NOUNS:CREDIT:1"), interaction.translate("misc:NOUNS:CREDIT:2"), interaction.translate("misc:NOUNS:CREDIT:5"))}` });
if (amount > data.memberData.money) return interaction.error("economy/slots:NOT_ENOUGH", { money: `**${amount}** ${client.getNoun(amount, interaction.translate("misc:NOUNS:CREDIT:1"), interaction.translate("misc:NOUNS:CREDIT:2"), interaction.translate("misc:NOUNS:CREDIT:5"))}` }, { edit: true });
const fruits = ["🍎", "🍐", "🍌", "🍇", "🍉", "🍒", "🍓"];
@ -100,6 +101,8 @@ class Slots extends BaseCommand {
const toAdd = credits - amount;
data.memberData.money += toAdd;
const info = {
user: interaction.translate("economy/slots:DESCRIPTION"),
amount: toAdd,
@ -107,7 +110,6 @@ class Slots extends BaseCommand {
type: "got"
};
data.memberData.transactions.push(info);
data.memberData.money += toAdd;
if (!data.userData.achievements.slots.achieved) {
data.userData.achievements.slots.progress.now += 1;

View file

@ -12,12 +12,12 @@ class TicTacToe extends BaseCommand {
command: new SlashCommandBuilder()
.setName("tictactoe")
.setDescription(client.translate("economy/tictactoe:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -42,9 +42,12 @@ class TicTacToe extends BaseCommand {
}).then(async winner => {
const memberData = await client.findOrCreateMember({
id: winner.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
memberData.money += 100;
await memberData.save();
const info = {
user: interaction.translate("economy/transactions:TTT"),
amount: 100,
@ -52,9 +55,6 @@ class TicTacToe extends BaseCommand {
type: "got"
};
memberData.transactions.push(info);
memberData.money += 100;
await memberData.save();
});
}
}

View file

@ -11,11 +11,11 @@ class Transactions extends BaseCommand {
command: new SlashCommandBuilder()
.setName("transactions")
.setDescription(client.translate("economy/transactions:DESCRIPTION"))
.setDMPermission(false)
.addBooleanOption(option => option.setName("clear")
.setDescription(client.translate("economy/transactions:CLEAR"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -10,10 +10,10 @@ class Work extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("work")
.setDescription(client.translate("economy/work:DESCRIPTION")),
.setDescription(client.translate("economy/work:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -98,16 +98,16 @@ class Work extends BaseCommand {
]);
}
data.memberData.money += won;
await data.memberData.save();
const info = {
user: interaction.translate("economy/work:SALARY"),
amount: won,
date: Date.now(),
type: "got"
};
data.memberData.transactions.push(info);
data.memberData.money += won;
await data.memberData.save();
const messageOptions = {
embeds: [embed]

View file

@ -11,13 +11,12 @@ class Eightball extends BaseCommand {
command: new SlashCommandBuilder()
.setName("8ball")
.setDescription(client.translate("fun/8ball:DESCRIPTION"))
.addStringOption(option =>
option.setName("question")
.setDescription(client.translate("fun/8ball:QUESTION"))
.setRequired(true)),
.setDMPermission(true)
.addStringOption(option => option.setName("question")
.setDescription(client.translate("fun/8ball:QUESTION"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -36,17 +35,18 @@ class Eightball extends BaseCommand {
*/
async execute(client, interaction) {
await interaction.deferReply();
const question = interaction.options.getString("question");
if (!question.endsWith("?")) return interaction.replyT("fun/8ball:ERR_QUESTION", null, { ephemeral: true });
const question = interaction.options.getString("question");
if (!question.endsWith("?")) return interaction.error("fun/8ball:ERR_QUESTION", null, { ephemeral: true });
const answerN = client.functions.randomNum(1, 20);
const answer = interaction.translate(`fun/8ball:RESPONSE_${answerN}`);
await client.wait(2000);
await client.wait(5000);
interaction.editReply({
content: answer
});
interaction.replyT("fun/8ball:ANSWER", {
question,
answer
}, { edit: true });
}
}

View file

@ -11,10 +11,10 @@ class Crab extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("crab")
.setDescription(client.translate("fun/crab:DESCRIPTION")),
.setDescription(client.translate("fun/crab:DESCRIPTION"))
.setDMPermission(true),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -1,5 +1,6 @@
const { SlashCommandBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
const BaseCommand = require("../../base/BaseCommand"),
fetch = require("node-fetch");
class LMGTFY extends BaseCommand {
/**
@ -11,13 +12,15 @@ class LMGTFY extends BaseCommand {
command: new SlashCommandBuilder()
.setName("lmgtfy")
.setDescription(client.translate("fun/lmgtfy:DESCRIPTION"))
.addStringOption(option =>
option.setName("question")
.setDescription(client.translate("fun/8ball:QUESTION"))
.setRequired(true)),
.setDMPermission(true)
.addStringOption(option => option.setName("query")
.setDescription(client.translate("fun/lmgtfy:QUERY"))
.setRequired(true))
.addBooleanOption(option => option.setName("short")
.setDescription(client.translate("fun/lmgtfy:SHORT"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -35,12 +38,23 @@ class LMGTFY extends BaseCommand {
* @param {Object} data
*/
async execute(client, interaction) {
const question = interaction.options.getString("question").replace(/[' '_]/g, "+");
const query = interaction.options.getString("query").replace(/[' '_]/g, "+"),
short = interaction.options.getBoolean("short"),
url = `https://letmegooglethat.com/?q=${query}`;
interaction.reply({
content: `<https://letmegooglethat.com/?q=${question}>`,
ephemeral: true
});
if (short) {
const res = await fetch(`https://is.gd/create.php?format=simple&url=${encodeURIComponent(url)}`).then(res => res.text());
interaction.reply({
content: `<${res}>`,
ephemeral: true
});
} else {
interaction.reply({
content: `<${url}>`,
ephemeral: true
});
}
}
}

View file

@ -12,6 +12,7 @@ class Lovecalc extends BaseCommand {
command: new SlashCommandBuilder()
.setName("lovecalc")
.setDescription(client.translate("fun/lovecalc:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option =>
option.setName("first_member")
.setDescription(client.translate("common:USER"))
@ -21,7 +22,6 @@ class Lovecalc extends BaseCommand {
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,10 +11,10 @@ class Memes extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("memes")
.setDescription(client.translate("fun/memes:DESCRIPTION")),
.setDescription(client.translate("fun/memes:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -49,13 +49,14 @@ class Memes extends BaseCommand {
.addOptions(tags)
);
await interaction.editReply({
const msg = await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
fetchReply: true,
components: [row]
});
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (2 * 60 * 1000) });
const collector = msg.createMessageComponentCollector({ filter, idle: (2 * 60 * 1000) });
collector.on("collect", async i => {
if (i.isSelectMenu() && i.customId === "memes_select") {

View file

@ -11,10 +11,10 @@ class Activity extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("activity")
.setDescription(client.translate("general/activity:DESCRIPTION")),
.setDescription(client.translate("general/activity:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Afk extends BaseCommand {
command: new SlashCommandBuilder()
.setName("afk")
.setDescription(client.translate("general/afk:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("message")
.setDescription(client.translate("common:MESSAGE"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,11 +11,11 @@ class Avatar extends BaseCommand {
command: new SlashCommandBuilder()
.setName("avatar")
.setDescription(client.translate("general/avatar:DESCRIPTION"))
.setDMPermission(true)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Emoji extends BaseCommand {
command: new SlashCommandBuilder()
.setName("emoji")
.setDescription(client.translate("general/emoji:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("emoji")
.setDescription(client.translate("common:EMOJI"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Help extends BaseCommand {
command: new SlashCommandBuilder()
.setName("help")
.setDescription(client.translate("general/help:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option =>
option.setName("command")
.setDescription(client.translate("owner/reload:COMMAND"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -50,7 +50,7 @@ class Help extends BaseCommand {
commands.forEach(c => {
if (!categories.includes(c.category)) {
if (c.category === "Owner" && interaction.member.id !== client.config.owner.id) return;
if (c.category === "Owner" && interaction.user.id !== client.config.owner.id) return;
categories.push(c.category);
}
});
@ -70,13 +70,14 @@ class Help extends BaseCommand {
.addOptions(categoriesRows)
);
await interaction.editReply({
const msg = await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
fetchReply: true,
components: [row]
});
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (15 * 1000) });
const collector = msg.createMessageComponentCollector({ filter, idle: (15 * 1000) });
collector.on("collect", async i => {
if (i.isSelectMenu() && (i.customId === "help_category_select" || i.customId === "help_commands_select")) {
@ -133,7 +134,10 @@ function getPermName(bitfield = 0) {
function generateCommandHelp(interaction, command) {
const cmd = interaction.client.commands.get(command);
if (!cmd) return interaction.error("general/help:NOT_FOUND", { search: command }, { edit: true });
if (!cmd) return interaction.error("general/help:NOT_FOUND", { command }, { edit: true });
const usage = interaction.translate(`${cmd.category.toLowerCase()}/${cmd.command.name}:USAGE`) === "" ?
interaction.translate("misc:NO_ARGS")
: interaction.translate(`${cmd.category.toLowerCase()}/${cmd.command.name}:USAGE`);
const embed = new EmbedBuilder()
.setAuthor({
@ -148,7 +152,7 @@ function generateCommandHelp(interaction, command) {
},
{
name: interaction.translate("general/help:FIELD_USAGE"),
value: interaction.translate(`${cmd.category.toLowerCase()}/${cmd.command.name}:USAGE`)
value: `*${cmd.guildOnly ? interaction.translate("general/help:GUILD_ONLY") : interaction.translate("general/help:NOT_GUILD_ONLY")}*\n\n` + usage
},
{
name: interaction.translate("general/help:FIELD_EXAMPLES"),

View file

@ -12,12 +12,12 @@ class Minecraft extends BaseCommand {
command: new SlashCommandBuilder()
.setName("minecraft")
.setDescription(client.translate("general/minecraft:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("ip")
.setDescription(client.translate("common:IP"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -10,10 +10,10 @@ class Ping extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("ping")
.setDescription(client.translate("general/ping:DESCRIPTION")),
.setDescription(client.translate("general/ping:DESCRIPTION"))
.setDMPermission(true),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -1,6 +1,7 @@
const { SlashCommandBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand"),
ms = require("ms");
ms = require("ms"),
moment = require("moment");
class Remindme extends BaseCommand {
/**
@ -12,6 +13,7 @@ class Remindme extends BaseCommand {
command: new SlashCommandBuilder()
.setName("remindme")
.setDescription(client.translate("general/remindme:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("time")
.setDescription(client.translate("owner/remindme:TIME"))
.setRequired(true))
@ -20,7 +22,6 @@ class Remindme extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -38,6 +39,8 @@ class Remindme extends BaseCommand {
* @param {Object} data
*/
async execute(client, interaction, data) {
await interaction.deferReply({ ephemeral: true });
const time = interaction.options.getString("time");
const message = interaction.options.getString("message");
const dateNow = Date.now();
@ -54,7 +57,10 @@ class Remindme extends BaseCommand {
data.userData.save();
client.databaseCache.usersReminds.set(interaction.member.id, data.userData);
interaction.success("general/remindme:SAVED");
interaction.success("general/remindme:SAVED", {
message,
time: moment(rData.createdAt).locale(interaction.guild.data.language).format("dddd, Do MMMM YYYY, HH:mm:ss")
}, { edit: true });
}
}

View file

@ -11,6 +11,7 @@ class Report extends BaseCommand {
command: new SlashCommandBuilder()
.setName("report")
.setDescription(client.translate("general/report:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true))
@ -19,7 +20,6 @@ class Report extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -10,10 +10,10 @@ class Serverinfo extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("serverinfo")
.setDescription(client.translate("general/serverinfo:DESCRIPTION")),
.setDescription(client.translate("general/serverinfo:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -12,12 +12,12 @@ class Shorturl extends BaseCommand {
command: new SlashCommandBuilder()
.setName("shorturl")
.setDescription(client.translate("general/shorturl:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("url")
.setDescription(client.translate("general/shorturl:URL"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -36,7 +36,7 @@ class Shorturl extends BaseCommand {
*/
async execute(client, interaction) {
const url = interaction.options.getString("url");
const res = await fetch(`https://is.gd/create.php?format=simple&url=${encodeURI(url)}`).then(res => res.text());
const res = await fetch(`https://is.gd/create.php?format=simple&url=${encodeURIComponent(url)}`).then(res => res.text());
interaction.reply({
content: `<${res}>`,

View file

@ -10,10 +10,10 @@ class Staff extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("staff")
.setDescription(client.translate("general/staff:DESCRIPTION")),
.setDescription(client.translate("general/staff:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,10 +11,10 @@ class Stats extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("stats")
.setDescription(client.translate("general/stats:DESCRIPTION")),
.setDescription(client.translate("general/stats:DESCRIPTION"))
.setDMPermission(true),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Suggest extends BaseCommand {
command: new SlashCommandBuilder()
.setName("suggest")
.setDescription(client.translate("general/suggest:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("message")
.setDescription(client.translate("common:MESSAGE"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,11 +11,11 @@ class Userinfo extends BaseCommand {
command: new SlashCommandBuilder()
.setName("userinfo")
.setDescription(client.translate("general/userinfo:DESCRIPTION"))
.setDMPermission(false)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -12,12 +12,12 @@ class Whois extends BaseCommand {
command: new SlashCommandBuilder()
.setName("whois")
.setDescription(client.translate("general/whois:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("ip")
.setDescription(client.translate("common:IP"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,6 +11,7 @@ class Clear extends BaseCommand {
command: new SlashCommandBuilder()
.setName("clear")
.setDescription(client.translate("moderation/clear:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addStringOption(option => option.setName("option")
.setDescription(client.translate("moderation/clear:OPTION"))
@ -19,7 +20,7 @@ class Clear extends BaseCommand {
.setDescription(client.translate("common:USER"))),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,13 +11,14 @@ class Clearwarns extends BaseCommand {
command: new SlashCommandBuilder()
.setName("clearwarns")
.setDescription(client.translate("moderation/clearwarns:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**
@ -38,7 +39,7 @@ class Clearwarns extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
memberData.sanctions = [];

View file

@ -1,4 +1,4 @@
const { SlashCommandBuilder, ActionRowBuilder, SelectMenuBuilder, InteractionCollector, ComponentType, PermissionFlagsBits } = require("discord.js");
const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand"),
ms = require("ms");
@ -12,20 +12,44 @@ class Giveaway extends BaseCommand {
command: new SlashCommandBuilder()
.setName("giveaway")
.setDescription(client.translate("moderation/giveaway:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addStringOption(option => option.setName("giveaway_id")
.setDescription(client.translate("moderation/giveaway:GIVEAWAY_ID")))
.addStringOption(option => option.setName("duration")
.setDescription(client.translate("common:DURATION")))
.addStringOption(option => option.setName("winners_count")
.setDescription(client.translate("moderation/giveaway:WINNERS_COUNT")))
.addStringOption(option => option.setName("prize")
.setDescription(client.translate("moderation/giveaway:PRIZE")))
.addBooleanOption(option => option.setName("isdrop")
.setDescription(client.translate("moderation/giveaway:ISDROP"))),
.addSubcommand(subcommand => subcommand.setName("create")
.setDescription(client.translate("moderation/giveaway:CREATE"))
.addStringOption(option => option.setName("duration")
.setDescription(client.translate("common:DURATION"))
.setRequired(true))
.addIntegerOption(option => option.setName("winners_count")
.setDescription(client.translate("moderation/giveaway:WINNERS_COUNT"))
.setRequired(true))
.addStringOption(option => option.setName("prize")
.setDescription(client.translate("moderation/giveaway:PRIZE"))
.setRequired(true))
.addBooleanOption(option => option.setName("isdrop")
.setDescription(client.translate("moderation/giveaway:ISDROP"))
.setRequired(true))
)
.addSubcommand(subcommand => subcommand.setName("reroll")
.setDescription(client.translate("moderation/giveaway:REROLL"))
.addStringOption(option => option.setName("giveaway_id")
.setDescription(client.translate("moderation/giveaway:GIVEAWAY_ID"))
.setRequired(true))
)
.addSubcommand(subcommand => subcommand.setName("end")
.setDescription(client.translate("moderation/giveaway:END"))
.addStringOption(option => option.setName("giveaway_id")
.setDescription(client.translate("moderation/giveaway:GIVEAWAY_ID"))
.setRequired(true))
)
.addSubcommand(subcommand => subcommand.setName("delete")
.setDescription(client.translate("moderation/giveaway:DELETE"))
.addStringOption(option => option.setName("giveaway_id")
.setDescription(client.translate("moderation/giveaway:GIVEAWAY_ID"))
.setRequired(true))
),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**
@ -42,154 +66,81 @@ class Giveaway extends BaseCommand {
* @param {Object} data
*/
async execute(client, interaction) {
const options = ["create", "reroll", "delete", "end"].map(tag =>
JSON.parse(JSON.stringify({
label: tag,
value: tag
}))
);
const command = interaction.options.getSubcommand();
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
.setCustomId("giveaway_select")
.setPlaceholder(client.translate("common:NOTHING_SELECTED"))
.addOptions(options)
);
if (command === "create") {
const currentGiveaways = client.giveawaysManager.giveaways.filter(g => g.guildId === interaction.guildId && !g.ended).length;
if (currentGiveaways > 5) return interaction.error("moderation/giveaway:MAX_COUNT");
const msg = await interaction.reply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
components: [row],
ephemeral: true,
fetchReply: true
});
const duration = interaction.options.getString("duration");
if (ms(duration) > ms("10d")) return interaction.error("moderation/giveaway:MAX_DURATION");
const filter = i => i.customId === "giveaway_select" && i.user.id === interaction.user.id;
const collector = new InteractionCollector(client, {
filter,
componentType: ComponentType.SelectMenu,
message: msg,
idle: (30 * 1000)
});
const winnersCount = interaction.options.getInteger("winners_count");
if (winnersCount > 10 || winnersCount < 1) return interaction.error("misc:INVALID_NUMBER_RANGE", { min: 1, max: 10 });
collector.on("collect", async i => {
const option = i?.values[0];
const prize = interaction.options.getString("prize");
const isdrop = interaction.options.getBoolean("isdrop");
if (option === "create") {
const currentGiveaways = client.giveawaysManager.giveaways.filter(g => g.guildId === interaction.guild.id && !g.ended).length;
if (currentGiveaways > 5) return i.update({ content: interaction.translate("moderation/giveaway:MAX_COUNT") });
const duration = interaction.options.getString("duration");
if (!duration) return i.update({ content: interaction.translate("moderation/giveaway:INVALID_CREATE") });
if (ms(duration) > ms("10d")) return i.update({ content: interaction.translate("moderation/giveaway:MAX_DURATION") });
const winnersCount = interaction.options.getString("winners_count");
if (!winnersCount) return i.update({ content: interaction.translate("moderation/giveaway:INVALID_CREATE") });
if (isNaN(winnersCount) || winnersCount > 10 || winnersCount < 1) return i.update({ content: interaction.translate("misc:INVALID_NUMBER_RANGE", { min: 1, max: 10 }) });
const prize = interaction.options.getString("prize");
if (!prize) return i.update({ content: interaction.translate("moderation/giveaway:INVALID_CREATE") });
const isdrop = interaction.options.getString("isdrop");
client.giveawaysManager.start(interaction.channel, {
duration: ms(duration),
winnerCount: winnersCount,
prize: prize,
hostedBy: interaction.user,
isDrop: isdrop,
messages: {
giveaway: interaction.translate("moderation/giveaway:TITLE"),
giveawayEnded: interaction.translate("moderation/giveaway:ENDED"),
timeRemaining: interaction.translate("moderation/giveaway:TIME_REMAINING"),
inviteToParticipate: interaction.translate("moderation/giveaway:INVITE_PARTICIPATE"),
winMessage: interaction.translate("moderation/giveaway:WIN_MESSAGE"),
drawing: interaction.translate("moderation/giveaway:DRAWING"),
dropMessage: interaction.translate("moderation/giveaway:DROP"),
embedFooter: interaction.translate("moderation/giveaway:FOOTER"),
noWinner: interaction.translate("moderation/giveaway:NO_WINNER"),
winners: interaction.translate("moderation/giveaway:WINNERS"),
endedAt: interaction.translate("moderation/giveaway:END_AT"),
hostedBy: interaction.translate("moderation/giveaway:HOSTEDBY"),
// units: {
// seconds: interaction.translate("time:SECONDS", {
// amount: ""
// }).trim(),
// minutes: interaction.translate("time:MINUTES", {
// amount: ""
// }).trim(),
// hours: interaction.translate("time:HOURS", {
// amount: ""
// }).trim(),
// days: interaction.translate("time:DAYS", {
// amount: ""
// }).trim()
// }
}
}).then(() => {
return i.update({
content: interaction.translate("moderation/giveaway:GIVEAWAY_CREATED"),
components: []
});
});
} else if (option === "reroll") {
const giveaway_id = interaction.options.getString("giveaway_id");
if (!giveaway_id) return i.update({ content: interaction.translate("moderation/giveaway:MISSING_ID"), components: [] });
client.giveawaysManager.reroll(giveaway_id, {
messages: {
congrat: interaction.translate("moderation/giveaway:REROLL_CONGRAT"),
error: interaction.translate("moderation/giveaway:REROLL_ERROR")
}
}).then(() => {
return i.update({
content: interaction.translate("moderation/giveaway:GIVEAWAY_REROLLED"),
components: []
});
}).catch(() => {
return i.update({
content: interaction.translate("moderation/giveaway:NOT_FOUND_ENDED", {
messageId: giveaway_id
}),
components: []
});
});
} else if (option === "delete") {
const giveaway_id = interaction.options.getString("giveaway_id");
if (!giveaway_id) return i.update({ content: interaction.translate("moderation/giveaway:MISSING_ID"), components: [] });
client.giveawaysManager.delete(giveaway_id).then(() => {
return i.update({
content: interaction.translate("moderation/giveaway:GIVEAWAY_DELETED"),
components: []
});
}).catch(() => {
return i.update({
content: interaction.translate("moderation/giveaway:NOT_FOUND", {
messageId: giveaway_id
}),
components: []
});
});
} else if (option === "end") {
const giveaway_id = interaction.options.getString("giveaway_id");
if (!giveaway_id) return i.update({ content: interaction.translate("moderation/giveaway:MISSING_ID"), components: [] });
try {
client.giveawaysManager.end(giveaway_id);
return i.update({
content: interaction.translate("moderation/giveaway:GIVEAWAY_ENDED"),
components: []
});
} catch (e) {
return i.update({
content: interaction.translate("moderation/giveaway:NOT_FOUND", {
messageId: giveaway_id
}),
components: []
});
client.giveawaysManager.start(interaction.channel, {
duration: ms(duration),
winnerCount: winnersCount,
prize: prize,
hostedBy: interaction.user,
isDrop: isdrop,
messages: {
giveaway: interaction.translate("moderation/giveaway:TITLE"),
giveawayEnded: interaction.translate("moderation/giveaway:ENDED"),
timeRemaining: interaction.translate("moderation/giveaway:TIME_REMAINING"),
inviteToParticipate: interaction.translate("moderation/giveaway:INVITE_PARTICIPATE"),
winMessage: interaction.translate("moderation/giveaway:WIN_MESSAGE"),
drawing: interaction.translate("moderation/giveaway:DRAWING"),
dropMessage: interaction.translate("moderation/giveaway:DROP"),
embedFooter: interaction.translate("moderation/giveaway:FOOTER"),
noWinner: interaction.translate("moderation/giveaway:NO_WINNER"),
winners: interaction.translate("moderation/giveaway:WINNERS"),
endedAt: interaction.translate("moderation/giveaway:END_AT"),
hostedBy: interaction.translate("moderation/giveaway:HOSTED_BY")
}
}).then(() => {
return interaction.success("moderation/giveaway:GIVEAWAY_CREATED", null, { ephemeral: true });
});
} else if (command === "reroll") {
const giveaway_id = interaction.options.getString("giveaway_id");
client.giveawaysManager.reroll(giveaway_id, {
messages: {
congrat: interaction.translate("moderation/giveaway:REROLL_CONGRAT"),
error: interaction.translate("moderation/giveaway:REROLL_ERROR")
}
}).then(() => {
return interaction.success("moderation/giveaway:GIVEAWAY_REROLLED");
}).catch(() => {
return interaction.error("moderation/giveaway:NOT_FOUND_ENDED", {
messageId: giveaway_id
}, { ephemeral: true });
});
} else if (command === "end") {
const giveaway_id = interaction.options.getString("giveaway_id");
try {
client.giveawaysManager.end(giveaway_id);
return interaction.success("moderation/giveaway:GIVEAWAY_ENDED");
} catch (e) {
return interaction.error("moderation/giveaway:NOT_FOUND", {
messageId: giveaway_id
}, { ephemeral: true });
}
});
} else if (command === "delete") {
const giveaway_id = interaction.options.getString("giveaway_id");
client.giveawaysManager.delete(giveaway_id).then(() => {
return interaction.success("moderation/giveaway:GIVEAWAY_DELETED");
}).catch(() => {
return interaction.error("moderation/giveaway:NOT_FOUND", {
messageId: giveaway_id
}, { ephemeral: true });
});
}
}
}

View file

@ -11,13 +11,14 @@ class Poll extends BaseCommand {
command: new SlashCommandBuilder()
.setName("poll")
.setDescription(client.translate("moderation/poll:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addStringOption(option => option.setName("question")
.setDescription(client.translate("moderation/poll:QUESTION"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,13 +11,14 @@ class Unban extends BaseCommand {
command: new SlashCommandBuilder()
.setName("unban")
.setDescription(client.translate("moderation/unban:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addStringOption(option => option.setName("user_id")
.setDescription(client.translate("moderation/unban:ID"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**

View file

@ -11,10 +11,11 @@ class Warn extends BaseCommand {
command: new ContextMenuCommandBuilder()
.setName("warn")
.setType(ApplicationCommandType.User)
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**
@ -40,12 +41,14 @@ class Warn extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
const modal = new ModalBuilder()
.setCustomId("warn_modal")
.setTitle(interaction.translate("moderation/warn:MODAL_TITLE"));
.setTitle(interaction.translate("moderation/warn:MODAL_TITLE", {
nickname: member.user.tag
}));
const reasonInput = new TextInputBuilder()
.setCustomId("warn_reason")

View file

@ -11,13 +11,14 @@ class Warns extends BaseCommand {
command: new SlashCommandBuilder()
.setName("warns")
.setDescription(client.translate("moderation/warns:DESCRIPTION"))
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers && PermissionFlagsBits.ManageMessages)
.addUserOption(option => option.setName("user")
.setDescription(client.translate("common:USER"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true
ownerOnly: false
});
}
/**
@ -39,7 +40,7 @@ class Warns extends BaseCommand {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
const embed = new EmbedBuilder()

View file

@ -10,10 +10,10 @@ class Back extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("back")
.setDescription(client.translate("music/back:DESCRIPTION")),
.setDescription(client.translate("music/back:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -1,4 +1,4 @@
const { SlashCommandBuilder, ActionRowBuilder, SelectMenuBuilder, } = require("discord.js"),
const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, EmbedBuilder, ButtonStyle } = require("discord.js"),
{ joinVoiceChannel, createAudioResource, createAudioPlayer, getVoiceConnection, AudioPlayerStatus } = require("@discordjs/voice");
const BaseCommand = require("../../base/BaseCommand"),
fs = require("fs");
@ -12,10 +12,13 @@ class Clips extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("clips")
.setDescription(client.translate("music/clips:DESCRIPTION")),
.setDescription(client.translate("music/clips:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("query")
.setDescription(client.translate("music/clips:QUERY"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -36,82 +39,191 @@ class Clips extends BaseCommand {
fs.readdir("./clips", async function (err, files) {
await interaction.deferReply();
if (err) return console.log("Unable to read directory: " + err);
const query = interaction.options.getString("query");
const clips = files.map(file => {
const fileName = file.substring(0, file.length - 4);
return {
label: fileName,
value: fileName
};
});
if (err) {
interaction.editReply({
content: "```js\n" + err + "```"
});
return console.log("Unable to read directory: " + err);
}
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
.setCustomId("clips_select")
.setPlaceholder(client.translate("common:NOTHING_SELECTED"))
.addOptions(clips)
);
if (query === "list") {
const clips = files.map(file => file.substring(0, file.length - 4));
let currentPage = 0;
const embeds = generateClipsEmbeds(interaction, clips);
await interaction.editReply({
content: interaction.translate("music/clips:AVAILABLE_CLIPS"),
components: [row]
});
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId("clips_prev_page")
.setStyle(ButtonStyle.Primary)
.setEmoji("⬅️"),
new ButtonBuilder()
.setCustomId("clips_next_page")
.setStyle(ButtonStyle.Primary)
.setEmoji("➡️"),
new ButtonBuilder()
.setCustomId("clips_jump_page")
.setStyle(ButtonStyle.Secondary)
.setEmoji("↗️"),
new ButtonBuilder()
.setCustomId("clips_stop")
.setStyle(ButtonStyle.Danger)
.setEmoji("⏹️"),
);
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (15 * 1000) });
await interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
components: [row]
});
collector.on("collect", async i => {
if (i.isSelectMenu() && i.customId === "clips_select") {
const clip = i?.values[0];
const voice = i.member.voice.channel;
if (!voice) return i.update({ content: interaction.translate("music/play:NO_VOICE_CHANNEL"), components: [] });
const queue = client.player.getQueue(i.guild.id);
if (queue) return i.update({ content: interaction.translate("music/clips:ACTIVE_QUEUE"), components: [] });
if (getVoiceConnection(i.guild.id)) return i.update({ content: interaction.translate("music/clips:ACTIVE_CLIP"), components: [] });
if (!fs.existsSync(`./clips/${clip}.mp3`)) return i.update({ content: interaction.translate("music/clips:NO_FILE", { file: clip }), components: [] });
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (20 * 1000) });
try {
const connection = joinVoiceChannel({
channelId: voice.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator
});
collector.on("collect", async i => {
if (i.isButton()) {
if (i.customId === "clips_prev_page") {
i.deferUpdate();
const resource = createAudioResource(fs.createReadStream(`./clips/${clip}.mp3`));
const player = createAudioPlayer()
.on("error", err => {
connection.destroy();
console.error(err.message);
if (currentPage !== 0) {
--currentPage;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
components: [row]
});
}
} else if (i.customId === "clips_next_page") {
i.deferUpdate();
if (currentPage < embeds.length - 1) {
currentPage++;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
components: [row]
});
}
} else if (i.customId === "clips_jump_page") {
i.deferUpdate();
const msg = await interaction.followUp({
content: interaction.translate("misc:JUMP_TO_PAGE", {
length: embeds.length
}),
fetchReply: true
});
player.play(resource);
connection.subscribe(player);
const filter = res => {
return res.author.id === interaction.user.id && !isNaN(res.content);
};
player.on(AudioPlayerStatus.Idle, () => {
connection.destroy();
});
} catch (error) {
console.error(error);
interaction.channel.awaitMessages({ filter, max: 1, time: (10 * 1000) }).then(collected => {
if (embeds[collected.first().content - 1]) {
currentPage = collected.first().content - 1;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
components: [row]
});
if (collected.first().deletable) collected.first().delete();
if (msg.deletable) msg.delete();
} else {
if (collected.first().deletable) collected.first().delete();
if (msg.deletable) msg.delete();
return;
}
});
} else if (i.customId === "clips_stop") {
i.deferUpdate();
collector.stop();
}
}
});
await interaction.editReply({
content: interaction.translate("music/clips:PLAYING", {
clip
}),
components: []
collector.on("end", () => {
row.components.forEach(component => {
component.setDisabled(true);
});
}
});
collector.on("end", () => {
return interaction.editReply({
return interaction.editReply({
components: [row]
});
});
} else {
const voice = interaction.member.voice.channel;
if (!voice) return interaction.editReply({ content: interaction.translate("music/play:NO_VOICE_CHANNEL") });
const queue = client.player.getQueue(interaction.guild.id);
if (queue) return interaction.editReply({ content: interaction.translate("music/clips:ACTIVE_QUEUE") });
if (getVoiceConnection(interaction.guild.id)) return interaction.editReply({ content: interaction.translate("music/clips:ACTIVE_CLIP") });
if (!fs.existsSync(`./clips/${query}.mp3`)) return interaction.editReply({ content: interaction.translate("music/clips:NO_FILE", { file: query }) });
try {
const connection = joinVoiceChannel({
channelId: voice.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator
});
const resource = createAudioResource(fs.createReadStream(`./clips/${query}.mp3`));
const player = createAudioPlayer()
.on("error", err => {
connection.destroy();
console.error(err.message);
});
player.play(resource);
connection.subscribe(player);
player.on(AudioPlayerStatus.Idle, () => {
connection.destroy();
});
} catch (error) {
console.error(error);
}
await interaction.editReply({
content: interaction.translate("music/clips:PLAYING", {
clip: query
}),
components: []
});
});
}
});
}
}
/**
*
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {Array} clips
* @returns
*/
function generateClipsEmbeds(interaction, clips) {
const embeds = [];
let k = 10;
for (let i = 0; i < clips.length; i += 10) {
const current = clips.slice(i, k);
k += 10;
const page = current.join("\n");
const embed = new EmbedBuilder()
.setColor(interaction.client.config.embed.color)
.setFooter({
text: interaction.client.config.embed.footer
})
.setTitle(interaction.translate("music/clips:CLIPS_LIST"))
.setDescription(page)
.setTimestamp();
embeds.push(embed);
}
return embeds;
}
module.exports = Clips;

View file

@ -1,5 +1,5 @@
const { SlashCommandBuilder, ActionRowBuilder, SelectMenuBuilder } = require("discord.js"),
{ QueueRepeatMode } = require("../../helpers/Music/dist/index");
const { SlashCommandBuilder } = require("discord.js"),
{ QueueRepeatMode } = require("discord-player-play-dl");
const BaseCommand = require("../../base/BaseCommand");
class Loop extends BaseCommand {
@ -11,10 +11,19 @@ class Loop extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("loop")
.setDescription(client.translate("music/loop:DESCRIPTION")),
.setDescription(client.translate("music/loop:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("option")
.setDescription(client.translate("economy/bank:OPTION"))
.setRequired(true)
.addChoices(
{ name: client.translate("music/loop:AUTOPLAY"), value: "3" },
{ name: client.translate("music/loop:QUEUE"), value: "2" },
{ name: client.translate("music/loop:TRACK"), value: "1" },
{ name: client.translate("music/loop:DISABLE"), value: "0" }
)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -32,64 +41,23 @@ class Loop extends BaseCommand {
* @param {Object} data
*/
async execute(client, interaction) {
await interaction.deferReply();
const voice = interaction.member.voice.channel;
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL");
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL", null, { edit: true });
const queue = client.player.getQueue(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING");
if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true });
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
.setCustomId("loop_select")
.setPlaceholder(client.translate("common:NOTHING_SELECTED"))
.addOptions([
{
label: client.translate("music/loop:AUTOPLAY"),
value: "3"
},
{
label: client.translate("music/loop:QUEUE"),
value: "2"
},
{
label: client.translate("music/loop:TRACK"),
value: "1"
},
{
label: client.translate("music/loop:DISABLE"),
value: "0"
}
])
);
const type = interaction.options.getString("option");
const mode = type === "3" ? QueueRepeatMode.AUTOPLAY :
type === "2" ? QueueRepeatMode.QUEUE :
type === "1" ? QueueRepeatMode.TRACK : QueueRepeatMode.OFF;
await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
components: [row]
});
queue.setRepeatMode(mode);
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (15 * 1000) });
collector.on("collect", async i => {
if (i.isSelectMenu() && i.customId === "loop_select") {
const type = i?.values[0];
const mode = type === "3" ? QueueRepeatMode.AUTOPLAY :
type === "2" ? QueueRepeatMode.QUEUE :
type === "1" ? QueueRepeatMode.TRACK : QueueRepeatMode.OFF;
queue.setRepeatMode(mode);
return interaction.editReply({
content: interaction.translate(`music/loop:${
type === "3" ? "AUTOPLAY_ENABLED" :
type === "2" ? "QUEUE_ENABLED" :
type === "1" ? "TRACK_ENABLED" : "LOOP_DISABLED"
}`),
components: []
});
}
});
interaction.success(`music/loop:${
type === "3" ? "AUTOPLAY_ENABLED" :
type === "2" ? "QUEUE_ENABLED" :
type === "1" ? "TRACK_ENABLED" : "LOOP_DISABLED"
}`);
}
}

View file

@ -1,5 +1,5 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"),
{ QueueRepeatMode } = require("../../helpers/Music/dist/index");
{ QueueRepeatMode } = require("discord-player-play-dl");
const BaseCommand = require("../../base/BaseCommand");
class Nowplaying extends BaseCommand {
@ -11,10 +11,10 @@ class Nowplaying extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("nowplaying")
.setDescription(client.translate("music/nowplaying:DESCRIPTION")),
.setDescription(client.translate("music/nowplaying:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -33,8 +33,9 @@ class Nowplaying extends BaseCommand {
*/
async execute(client, interaction) {
await interaction.deferReply();
const queue = client.player.getQueue(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING");
if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true });
const progressBar = queue.createProgressBar();
const track = queue.current;

View file

@ -1,5 +1,4 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } = require("discord.js"),
{ QueryType } = require("../../helpers/Music/dist/index");
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Play extends BaseCommand {
@ -12,12 +11,12 @@ class Play extends BaseCommand {
command: new SlashCommandBuilder()
.setName("play")
.setDescription(client.translate("music/play:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("query")
.setDescription(client.translate("music/play:QUERY"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -38,40 +37,18 @@ class Play extends BaseCommand {
await interaction.deferReply();
const voice = interaction.member.voice.channel;
if (!voice) return interaction.editReply({ content: interaction.translate("music/play:NO_VOICE_CHANNEL") });
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL", null, { edit: true });
const query = interaction.options.getString("query");
const perms = voice.permissionsFor(client.user);
if (!perms.has(PermissionsBitField.Flags.Connect) || !perms.has(PermissionsBitField.Flags.Speak)) return interaction.editReply({ content: interaction.translate("music/play:VOICE_CHANNEL_CONNECT") });
if (!perms.has(PermissionsBitField.Flags.Connect) || !perms.has(PermissionsBitField.Flags.Speak)) return interaction.error("music/play:VOICE_CHANNEL_CONNECT", null, { edit: true });
try {
var searchResult;
if (!query.includes("http")) {
const search = await playdl.search(query, { limit: 10 });
var searchResult = await client.player.search(query, {
requestedBy: interaction.user
});
if (search) {
const found = search.map(track => new Track(client.player, {
title: track.title,
duration: Util.buildTimeCode(Util.parseMS(track.durationInSec * 1000)),
thumbnail: track.thumbnails[0].url || "https://cdn.discordapp.com/attachments/708642702602010684/1012418217660121089/noimage.png",
views: track.views,
author: track.channel.name,
description: "search",
url: track.url,
requestedBy: interaction.user,
playlist: null,
source: "youtube"
}));
searchResult = { playlist: null, tracks: found, searched: true };
}
} else {
searchResult = await client.player.search(query, {
requestedBy: interaction.user
});
if (!searchResult.tracks[0] || !searchResult)
return interaction.editReply({ content: interaction.translate("music/play:NO_RESULT", { query, error: "Unknown Error" }) });
}
if (!searchResult.tracks[0] || !searchResult)
return interaction.error("music/play:NO_RESULT", { query, error: "Скорее всего видео заблокировано по региону" }, { edit: true });
} catch (error) {
console.log(error);
return interaction.editReply({
@ -87,7 +64,7 @@ class Play extends BaseCommand {
autoSelfDeaf: true,
leaveOnEnd: true,
leaveOnStop: true,
bufferingTimeout: 1000,
bufferingTimeout: 1000
});
if (searchResult.searched) {
@ -215,7 +192,7 @@ class Play extends BaseCommand {
components: [row1, row2, row3]
});
collector.end();
collector.stop();
return;
}
}

View file

@ -1,5 +1,5 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"),
{ QueueRepeatMode } = require("../../helpers/Music/dist/index");
{ QueueRepeatMode } = require("discord-player-play-dl");
const BaseCommand = require("../../base/BaseCommand");
class Queue extends BaseCommand {
@ -11,10 +11,10 @@ class Queue extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("queue")
.setDescription(client.translate("music/queue:DESCRIPTION")),
.setDescription(client.translate("music/queue:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -102,7 +102,7 @@ class Queue extends BaseCommand {
if (embeds != generateQueueEmbeds(interaction, queue)) embeds = generateQueueEmbeds(interaction, queue);
const msg = await interaction.followUp({
content: interaction.translate("music/queue:PAGE_TO_JUMP", {
content: interaction.translate("misc:JUMP_TO_PAGE", {
length: embeds.length
}),
fetchReply: true
@ -151,7 +151,7 @@ class Queue extends BaseCommand {
/**
*
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {import("../../helpers/Music/dist/index").Queue} queue
* @param {import("discord-player-play-dl").Queue} queue
* @returns
*/
function generateQueueEmbeds(interaction, queue) {

44
commands/Music/shuffle.js Normal file
View file

@ -0,0 +1,44 @@
const { SlashCommandBuilder } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Shuffle extends BaseCommand {
/**
*
* @param {import("../base/JaBa")} client
*/
constructor(client) {
super({
command: new SlashCommandBuilder()
.setName("shuffle")
.setDescription(client.translate("music/shuffle:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
ownerOnly: false
});
}
/**
*
* @param {import("../../base/JaBa")} client
*/
async onLoad() {
//...
}
/**
*
* @param {import("../../base/JaBa")} client
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {Object} data
*/
async execute(client, interaction) {
const voice = interaction.member.voice.channel;
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL", null, { ephemeral: true });
const queue = client.player.getQueue(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { ephemeral: true });
const shuffled = queue.shuffle();
if (shuffled) interaction.success("music/shuffle:SUCCESS");
}
}
module.exports = Shuffle;

View file

@ -10,10 +10,10 @@ class Skip extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("skip")
.setDescription(client.translate("music/skip:DESCRIPTION")),
.setDescription(client.translate("music/skip:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,12 +11,12 @@ class Skipto extends BaseCommand {
command: new SlashCommandBuilder()
.setName("skipto")
.setDescription(client.translate("music/skipto:DESCRIPTION"))
.setDMPermission(false)
.addIntegerOption(option => option.setName("position")
.setDescription(client.translate("music/skipto:POSITION"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -10,10 +10,10 @@ class Stop extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("stop")
.setDescription(client.translate("music/stop:DESCRIPTION")),
.setDescription(client.translate("music/stop:DESCRIPTION"))
.setDMPermission(false),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}

View file

@ -11,10 +11,10 @@ class NSFW extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("nsfw")
.setDescription(client.translate("nsfw/nsfw:DESCRIPTION")),
.setDescription(client.translate("nsfw/nsfw:DESCRIPTION"))
.setDMPermission(true),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: false
});
}
@ -34,7 +34,7 @@ class NSFW extends BaseCommand {
async execute(client, interaction) {
await interaction.deferReply({ ephemeral: true });
if (!interaction.channel.nsfw) return interaction.replyT("misc:NSFW_COMMAND", null, { ephemeral: true, edit: true });
if ((interaction.guildId && !interaction.channel.nsfw)) return interaction.replyT("misc:NSFW_COMMAND", null, { ephemeral: true, edit: true });
const tags = ["hentai", "ecchi", "lewdanimegirls", "hentaifemdom", "animefeets", "animebooty", "biganimetiddies", "sideoppai", "ahegao"].map(tag =>
JSON.parse(JSON.stringify({
@ -51,14 +51,15 @@ class NSFW extends BaseCommand {
.addOptions(tags)
);
await interaction.editReply({
const msg = await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
ephemeral: true,
fetchReply: true,
components: [row]
});
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (2 * 60 * 1000) });
const collector = msg.createMessageComponentCollector({ filter, idle: (2 * 60 * 1000) });
collector.on("collect", async i => {
if (i.isSelectMenu() && i.customId === "nsfw_select") {

View file

@ -11,6 +11,7 @@ class Announcement extends BaseCommand {
command: new SlashCommandBuilder()
.setName("announcement")
.setDescription(client.translate("owner/announcement:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("message")
.setDescription(client.translate("common:MESSAGE"))
.setRequired(true))
@ -19,7 +20,6 @@ class Announcement extends BaseCommand {
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}

View file

@ -11,6 +11,7 @@ class Debug extends BaseCommand {
command: new SlashCommandBuilder()
.setName("debug")
.setDescription(client.translate("owner/debug:DESCRIPTION"))
.setDMPermission(false)
.addSubcommand(subcommand => subcommand.setName("set")
.setDescription(client.translate("owner/debug:SET"))
.addStringOption(option => option.setName("type")
@ -51,7 +52,6 @@ class Debug extends BaseCommand {
),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}
@ -68,7 +68,7 @@ class Debug extends BaseCommand {
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {Array} data
*/
async execute(client, interaction,) {
async execute(client, interaction) {
const command = interaction.options.getSubcommand();
if (command === "set") {
@ -79,7 +79,8 @@ class Debug extends BaseCommand {
id: member.id
});
const memberData = await client.findOrCreateMember({
id: member.id
id: member.id,
guildId: interaction.guildId
});
const int = interaction.options.getInteger("int");
@ -137,7 +138,8 @@ class Debug extends BaseCommand {
id: member.id
});
const memberData = await client.findOrCreateMember({
id: member.id
id: member.id,
guildId: interaction.guildId
});
const int = interaction.options.getInteger("int");

View file

@ -11,12 +11,12 @@ class Eval extends BaseCommand {
command: new SlashCommandBuilder()
.setName("eval")
.setDescription(client.translate("owner/eval:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("code")
.setDescription(client.translate("owner/eval:CODE"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}

View file

@ -13,12 +13,12 @@ class Reload extends BaseCommand {
command: new SlashCommandBuilder()
.setName("reload")
.setDescription(client.translate("owner/reload:DESCRIPTION"))
.setDMPermission(true)
.addStringOption(option => option.setName("command")
.setDescription(client.translate("owner/reload:COMMAND"))
.setRequired(true)),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}

View file

@ -11,6 +11,7 @@ class Say extends BaseCommand {
command: new SlashCommandBuilder()
.setName("say")
.setDescription(client.translate("owner/say:DESCRIPTION"))
.setDMPermission(false)
.addStringOption(option => option.setName("message")
.setDescription(client.translate("common:MESSAGE"))
.setRequired(true))
@ -18,7 +19,6 @@ class Say extends BaseCommand {
.setDescription(client.translate("common:CHANNEL"))),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}

View file

@ -10,10 +10,10 @@ class Servers extends BaseCommand {
super({
command: new SlashCommandBuilder()
.setName("servers")
.setDescription(client.translate("owner/servers:DESCRIPTION")),
.setDescription(client.translate("owner/servers:DESCRIPTION"))
.setDMPermission(true),
aliases: [],
dirname: __dirname,
guildOnly: true,
ownerOnly: true
});
}
@ -93,7 +93,7 @@ class Servers extends BaseCommand {
i.deferUpdate();
const msg = await interaction.followUp({
content: interaction.translate("music/queue:PAGE_TO_JUMP", {
content: interaction.translate("misc:JUMP_TO_PAGE", {
length: embeds.length
}),
fetchReply: true
@ -142,7 +142,7 @@ class Servers extends BaseCommand {
/**
*
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {*} servers
* @param {Array} servers
* @returns
*/
function generateServersEmbeds(interaction, servers) {
@ -150,7 +150,7 @@ function generateServersEmbeds(interaction, servers) {
let k = 10;
for (let i = 0; i < servers.size; i += 10) {
const current = servers.map(g => g).slice(i, k).sort((a, b) => b.memberCount - a.memberCount);
const current = servers.sort((a, b) => b.memberCount - a.memberCount).map(g => g).slice(i, k);
let j = i;
k += 10;
@ -170,12 +170,4 @@ function generateServersEmbeds(interaction, servers) {
return embeds;
}
// `${interaction.translate("common:SERVERS")}: ${interaction.client.guilds.cache.size}\n\n` +
// interaction.client.guilds.cache
// .sort((a, b) => b.memberCount - a.memberCount)
// .map(g => g)
// .map((g, i) => `${i + 1}. ${g.name} | ${g.memberCount} ${interaction.client.getNoun(g.memberCount, interaction.translate("misc:NOUNS:MEMBERS:1"), interaction.translate("misc:NOUNS:MEMBERS:2"), interaction.translate("misc:NOUNS:MEMBERS:5"))}`)
// .slice(i, k)
// .join("\n")
module.exports = Servers;

View file

@ -3,9 +3,9 @@ module.exports = {
token: "XXXXXXXXXXX",
/* ID of Bot's user */
user: "XXXXXXXXXXX",
/* For the support server */
/* Set to true for production */
production: true,
/* For the support server */
support: {
id: "XXXXXXXXXXX", // The ID of the support server
logs: "XXXXXXXXXXX", // And the ID of the logs channel of your server (new servers for example)

View file

@ -1,3 +1,63 @@
### JaBa v4.1.13
* Изменения
* Переписана команда *clips*.
### JaBa v4.1.12
* Исправления
* Фикс поиска по ссылкам.
* Фикс воспроизведения с SoundCloud.
### JaBa v4.1.11
* Изменения
* Команды которые нельзя использовать в ЛС с ботом не будут там отображаться.
* Исправления
* Переписаны команды *nsfw* и *memes* для работы в ЛС с ботом.
### JaBa v4.1.10
* Добавлено
* Команда *shuffle* - Перемешать очередь.
* Изменения
* Многие команды теперь можно использовать в ЛС с ботом. Узнать где именно можно использовать команду через *help*.
### JaBa v4.1.9
* Изменения
* Переписана система опыта. Теперь при достижении нового уровня опыт сбрасывается и бот оповещает о получении нового уровня.
* Исправления
* Команды *set* и *debug* ничего не делали.
### JaBa v4.1.8
* Добавлено
* Возможность сразу сократить ссылку в команде *lmgtfy*.
* Изменения
* Переписаны подсказки к командам.
* Переписана команда *giveaway*.
* Исправления
* Фикс ошибки *shorturl* с некоторыми ссылками.
### JaBa v4.1.7
* Изменения
* Переписана команда *leaderboard*.
### JaBa v4.1.6
* Изменения
* Изменён способ указания типа повтора в *loop*. Теперь вы указываете тип аргументом (подсказки имеются), а не из выпадающего списка в отдельном сообщении. Это одновременно удобно, быстро и меньше кода =)
* Исправления
* Фиксы в *tictactoe*.
### JaBa v4.1.5
* Изменения
* Более подробные сообщения в *remindme*.
### JaBa v4.1.4
* Исправления
* Ошибки в clips, loop, nowplaying и play.
### JaBa v4.1.3
* Добавлено
* Возможность принудительной очистки транзакций с помощью *transactions clear:True*

View file

@ -17,7 +17,7 @@ router.get("/:serverID", CheckAuth, async(req, res) => {
// Fetch guild informations
const guildInfos = await utils.fetchGuild(guild.id, req.client, req.user.guilds);
const memberData = await req.client.findOrCreateMember({ id: req.userInfos.id, guildID: guild.id });
const memberData = await req.client.findOrCreateMember({ id: req.userInfos.id, guildId: guild.id });
res.render("manager/guild", {
guild: guildInfos,

View file

@ -14,7 +14,7 @@ router.get("/:serverID", CheckAuth, async (req, res) => {
});
}
const memberData = await req.client.findOrCreateMember({ id: req.userInfos.id, guildID: guild.id });
const memberData = await req.client.findOrCreateMember({ id: req.userInfos.id, guildId: guild.id });
// Fetch guild informations
const membersData = await req.client.membersData.find({

View file

@ -26,7 +26,7 @@ class CommandHandler extends BaseEvent {
data.userData = userData;
if (command.guildOnly && !interaction.inGuild()) return interaction.replyT("misc:GUILD_ONLY", { ephemeral: true });
if (command.ownerOnly && interaction.member.id !== client.config.owner.id) return interaction.replyT("misc:OWNER_ONLY", { ephemeral: true });
if (command.ownerOnly && interaction.user.id !== client.config.owner.id) return interaction.replyT("misc:OWNER_ONLY", { ephemeral: true });
if (interaction.inGuild()) {
const guildData = await client.findOrCreateGuild({
@ -36,7 +36,7 @@ class CommandHandler extends BaseEvent {
const memberData = await client.findOrCreateMember({
id: interaction.member.id,
guildID: interaction.guildId
guildId: interaction.guildId
});
data.memberData = memberData;
}
@ -46,7 +46,8 @@ class CommandHandler extends BaseEvent {
userData.achievements.firstCommand.achieved = true;
userData.markModified("achievements.firstCommand");
await userData.save();
await interaction.followUp({
await interaction.channel.send({
content: interaction.user.toString(),
files: [{
name: "achievement_unlocked2.png",
attachment: "./assets/img/achievements/achievement_unlocked2.png"

View file

@ -40,7 +40,7 @@ class GuildMemberAdd extends BaseEvent {
const memberData = await client.findOrCreateMember({
id: member.id,
guildID: member.guild.id
guildId: member.guild.id
});
if (memberData.mute.muted && memberData.mute.endDate > Date.now()) {
member.guild.channels.cache.forEach((channel) => {

View file

@ -33,7 +33,7 @@ class MessageCreate extends BaseEvent {
if (message.guild) {
const memberData = await client.findOrCreateMember({
id: message.author.id,
guildID: message.guild.id
guildId: message.guild.id
});
data.memberData = memberData;
}
@ -61,7 +61,7 @@ class MessageCreate extends BaseEvent {
await data.userData.save();
message.replyT("general/afk:DELETED", {
username: message.author.username
});
}, { mention: true });
}
message.mentions.users.forEach(async (u) => {
@ -75,24 +75,37 @@ class MessageCreate extends BaseEvent {
}
}
/**
*
* @param {import("../base/JaBa")} client
* @param {import("discord.js").Message} msg
* @param {*} data
* @returns
*/
async function updateXp(client, msg, data) {
const points = parseInt(data.memberData.exp);
const level = parseInt(data.memberData.level);
const isInCooldown = xpCooldown[msg.author.id];
const points = parseInt(data.memberData.exp),
level = parseInt(data.memberData.level),
isInCooldown = xpCooldown[msg.author.id];
if (isInCooldown) {
if (isInCooldown > Date.now()) return;
}
const toWait = Date.now() + 60000; // 1 min
const toWait = Date.now() + (60 * 1000); // 1 min
xpCooldown[msg.author.id] = toWait;
const won = client.functions.randomNum(1, 4);
const won = client.functions.randomNum(1, 2);
const newXp = parseInt(points + won, 10);
const neededXp = 5 * (level * level) + 80 * level + 100;
if (newXp > neededXp) data.memberData.level = parseInt(level + 1, 10);
if (newXp > neededXp) {
data.memberData.level = parseInt(level + 1, 10);
data.memberData.exp = 0;
msg.replyT("misc:LEVEL_UP", {
level: data.memberData.level
}, { mention: false });
} else data.memberData.exp = parseInt(newXp, 10);
data.memberData.exp = parseInt(newXp, 10);
await data.memberData.save();
}

View file

@ -18,31 +18,24 @@ class Ready extends BaseEvent {
let tUsers = client.users.cache.size - hiddenGuild.memberCount;
let tServers = client.guilds.cache.size - 1;
// Logs some informations using logger
client.logger.log(`Loaded a total of ${commands.length} command(s).`, "ready");
client.logger.log(`${client.user.tag}, ready to serve ${tUsers} users in ${tServers} servers.`, "ready");
client.logger.log(`Invite Link: ${client.generateInvite({ scopes: ["bot", "applications.commands"] , permissions: [ PermissionsBitField.Flags.Administrator ] })}`, "ready");
// Birthday Announce
const birthdays = require("../helpers/birthdays");
birthdays.init(client);
// Unmute users
const checkUnmutes = require("../helpers/checkUnmutes");
checkUnmutes.init(client);
// Send reminds
const checkReminds = require("../helpers/checkReminds");
checkReminds.init(client);
// Clear transactions
const cleanup = require("../helpers/cleanup");
cleanup.init(client);
// Start the dashboard
if (client.config.dashboard.enabled) client.dashboard.init(client);
// Update status
const version = require("../package.json").version;
const status = [
{ name: "help", type: ActivityType.Watching },

View file

@ -1,4 +0,0 @@
node_modules/
dist/
*.d.ts

View file

@ -1,22 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": {
"node": true
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/ban-ts-comment": "error",
"semi": "error",
"no-console": "error"
}
}

View file

@ -1,96 +0,0 @@
import { Client, Collection, GuildResolvable } from "discord.js";
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
import { Queue } from "./Structures/Queue";
import { VoiceUtils } from "./VoiceInterface/VoiceUtils";
import { PlayerEvents, PlayerOptions, SearchOptions, PlayerInitOptions, PlayerSearchResult, PlaylistInitData } from "./types/types";
import Track from "./Structures/Track";
import { Playlist } from "./Structures/Playlist";
import { ExtractorModel } from "./Structures/ExtractorModel";
declare class Player extends EventEmitter<PlayerEvents> {
readonly client: Client;
readonly options: PlayerInitOptions;
readonly queues: Collection<string, Queue<unknown>>;
readonly voiceUtils: VoiceUtils;
readonly extractors: Collection<string, ExtractorModel>;
requiredEvents: string[];
/**
* Creates new Discord Player
* @param {Client} client The Discord Client
* @param {PlayerInitOptions} [options] The player init options
*/
constructor(client: Client, options?: PlayerInitOptions);
/**
* Handles voice state update
* @param {VoiceState} oldState The old voice state
* @param {VoiceState} newState The new voice state
* @returns {void}
* @private
*/
private _handleVoiceState;
/**
* Creates a queue for a guild if not available, else returns existing queue
* @param {GuildResolvable} guild The guild
* @param {PlayerOptions} queueInitOptions Queue init options
* @returns {Queue}
*/
createQueue<T = unknown>(guild: GuildResolvable, queueInitOptions?: PlayerOptions & {
metadata?: T;
}): Queue<T>;
/**
* Returns the queue if available
* @param {GuildResolvable} guild The guild id
* @returns {Queue}
*/
getQueue<T = unknown>(guild: GuildResolvable): Queue<T>;
/**
* Deletes a queue and returns deleted queue object
* @param {GuildResolvable} guild The guild id to remove
* @returns {Queue}
*/
deleteQueue<T = unknown>(guild: GuildResolvable): Queue<T>;
/**
* @typedef {object} PlayerSearchResult
* @property {Playlist} [playlist] The playlist (if any)
* @property {Track[]} tracks The tracks
*/
/**
* Search tracks
* @param {string|Track} query The search query
* @param {SearchOptions} options The search options
* @returns {Promise<PlayerSearchResult>}
*/
search(query: string | Track, options: SearchOptions): Promise<PlayerSearchResult>;
/**
* Registers extractor
* @param {string} extractorName The extractor name
* @param {ExtractorModel|any} extractor The extractor object
* @param {boolean} [force=false] Overwrite existing extractor with this name (if available)
* @returns {ExtractorModel}
*/
use(extractorName: string, extractor: ExtractorModel | any, force?: boolean): ExtractorModel;
/**
* Removes registered extractor
* @param {string} extractorName The extractor name
* @returns {ExtractorModel}
*/
unuse(extractorName: string): ExtractorModel;
/**
* Generates a report of the dependencies used by the `@discordjs/voice` module. Useful for debugging.
* @returns {string}
*/
scanDeps(): string;
emit<U extends keyof PlayerEvents>(eventName: U, ...args: Parameters<PlayerEvents[U]>): boolean;
/**
* Resolves queue
* @param {GuildResolvable|Queue} queueLike Queue like object
* @returns {Queue}
*/
resolveQueue<T>(queueLike: GuildResolvable | Queue): Queue<T>;
[Symbol.iterator](): Generator<Queue<unknown>, void, undefined>;
/**
* Creates `Playlist` instance
* @param data The data to initialize a playlist
*/
createPlaylist(data: PlaylistInitData): Playlist;
}
export { Player };

View file

@ -1,579 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Player = void 0;
const tslib_1 = require("tslib");
const discord_js_1 = require("discord.js");
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const Queue_1 = require("./Structures/Queue");
const VoiceUtils_1 = require("./VoiceInterface/VoiceUtils");
const types_1 = require("./types/types");
const Track_1 = tslib_1.__importDefault(require("./Structures/Track"));
const play_dl_1 = tslib_1.__importDefault(require("play-dl"));
const spotify_url_info_1 = tslib_1.__importDefault(require("spotify-url-info"));
const QueryResolver_1 = require("./utils/QueryResolver");
const Util_1 = require("./utils/Util");
const PlayerError_1 = require("./Structures/PlayerError");
const Playlist_1 = require("./Structures/Playlist");
const ExtractorModel_1 = require("./Structures/ExtractorModel");
const voice_1 = require("@discordjs/voice");
class Player extends tiny_typed_emitter_1.TypedEmitter {
/**
* Creates new Discord Player
* @param {Client} client The Discord Client
* @param {PlayerInitOptions} [options] The player init options
*/
constructor(client, options = {}) {
super();
this.options = {
autoRegisterExtractor: true,
connectionTimeout: 20000
};
this.queues = new discord_js_1.Collection();
this.voiceUtils = new VoiceUtils_1.VoiceUtils();
this.extractors = new discord_js_1.Collection();
this.requiredEvents = ["error", "connectionError"];
/**
* The discord.js client
* @type {Client}
*/
this.client = client;
if (this.client?.options?.intents && !new discord_js_1.IntentsBitField(this.client?.options?.intents).has(discord_js_1.IntentsBitField.Flags.GuildVoiceStates)) {
throw new PlayerError_1.PlayerError('client is missing "GuildVoiceStates" intent');
}
/**
* The extractors collection
* @type {ExtractorModel}
*/
this.options = Object.assign(this.options, options);
this.client.on("voiceStateUpdate", this._handleVoiceState.bind(this));
if (this.options?.autoRegisterExtractor) {
let nv; // eslint-disable-line @typescript-eslint/no-explicit-any
if ((nv = Util_1.Util.require("@discord-player/extractor"))) {
["Attachment", "Facebook", "Reverbnation", "Vimeo"].forEach((ext) => void this.use(ext, nv[ext]));
}
}
}
/**
* Handles voice state update
* @param {VoiceState} oldState The old voice state
* @param {VoiceState} newState The new voice state
* @returns {void}
* @private
*/
_handleVoiceState(oldState, newState) {
const queue = this.getQueue(oldState.guild.id);
if (!queue || !queue.connection)
return;
if (oldState.channelId && !newState.channelId && newState.member.id === newState.guild.members.me.id) {
try {
queue.destroy();
}
catch {
/* noop */
}
return void this.emit("botDisconnect", queue);
}
if (!oldState.channelId && newState.channelId && newState.member.id === newState.guild.members.me.id) {
if (!oldState.serverMute && newState.serverMute) {
// state.serverMute can be null
queue.setPaused(!!newState.serverMute);
}
else if (!oldState.suppress && newState.suppress) {
// state.suppress can be null
queue.setPaused(!!newState.suppress);
if (newState.suppress) {
newState.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
}
}
}
if (oldState.channelId === newState.channelId && newState.member.id === newState.guild.members.me.id) {
if (!oldState.serverMute && newState.serverMute) {
// state.serverMute can be null
queue.setPaused(!!newState.serverMute);
}
else if (!oldState.suppress && newState.suppress) {
// state.suppress can be null
queue.setPaused(!!newState.suppress);
if (newState.suppress) {
newState.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
}
}
}
if (queue.connection && !newState.channelId && oldState.channelId === queue.connection.channel.id) {
if (!Util_1.Util.isVoiceEmpty(queue.connection.channel))
return;
const timeout = setTimeout(() => {
if (!Util_1.Util.isVoiceEmpty(queue.connection.channel))
return;
if (!this.queues.has(queue.guild.id))
return;
if (queue.options.leaveOnEmpty)
queue.destroy(true);
this.emit("channelEmpty", queue);
}, queue.options.leaveOnEmptyCooldown || 0).unref();
queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
}
if (queue.connection && newState.channelId && newState.channelId === queue.connection.channel.id) {
const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
if (!channelEmpty && emptyTimeout) {
clearTimeout(emptyTimeout);
queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
}
}
if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId && newState.member.id === newState.guild.members.me.id) {
if (queue.connection && newState.member.id === newState.guild.members.me.id)
queue.connection.channel = newState.channel;
const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
const channelEmpty = Util_1.Util.isVoiceEmpty(queue.connection.channel);
if (!channelEmpty && emptyTimeout) {
clearTimeout(emptyTimeout);
queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
}
else {
const timeout = setTimeout(() => {
if (queue.connection && !Util_1.Util.isVoiceEmpty(queue.connection.channel))
return;
if (!this.queues.has(queue.guild.id))
return;
if (queue.options.leaveOnEmpty)
queue.destroy(true);
this.emit("channelEmpty", queue);
}, queue.options.leaveOnEmptyCooldown || 0).unref();
queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
}
}
}
/**
* Creates a queue for a guild if not available, else returns existing queue
* @param {GuildResolvable} guild The guild
* @param {PlayerOptions} queueInitOptions Queue init options
* @returns {Queue}
*/
createQueue(guild, queueInitOptions = {}) {
guild = this.client.guilds.resolve(guild);
if (!guild)
throw new PlayerError_1.PlayerError("Unknown Guild", PlayerError_1.ErrorStatusCode.UNKNOWN_GUILD);
if (this.queues.has(guild.id))
return this.queues.get(guild.id);
const _meta = queueInitOptions.metadata;
delete queueInitOptions["metadata"];
queueInitOptions.volumeSmoothness ?? (queueInitOptions.volumeSmoothness = 0.08);
const queue = new Queue_1.Queue(this, guild, queueInitOptions);
queue.metadata = _meta;
this.queues.set(guild.id, queue);
return queue;
}
/**
* Returns the queue if available
* @param {GuildResolvable} guild The guild id
* @returns {Queue}
*/
getQueue(guild) {
guild = this.client.guilds.resolve(guild);
if (!guild)
throw new PlayerError_1.PlayerError("Unknown Guild", PlayerError_1.ErrorStatusCode.UNKNOWN_GUILD);
return this.queues.get(guild.id);
}
/**
* Deletes a queue and returns deleted queue object
* @param {GuildResolvable} guild The guild id to remove
* @returns {Queue}
*/
deleteQueue(guild) {
guild = this.client.guilds.resolve(guild);
if (!guild)
throw new PlayerError_1.PlayerError("Unknown Guild", PlayerError_1.ErrorStatusCode.UNKNOWN_GUILD);
const prev = this.getQueue(guild);
try {
prev.destroy();
}
catch { } // eslint-disable-line no-empty
this.queues.delete(guild.id);
return prev;
}
/**
* @typedef {object} PlayerSearchResult
* @property {Playlist} [playlist] The playlist (if any)
* @property {Track[]} tracks The tracks
*/
/**
* Search tracks
* @param {string|Track} query The search query
* @param {SearchOptions} options The search options
* @returns {Promise<PlayerSearchResult>}
*/
async search(query, options) {
if (query instanceof Track_1.default)
return { playlist: query.playlist || null, tracks: [query] };
if (!options)
throw new PlayerError_1.PlayerError("DiscordPlayer#search needs search options!", PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
options.requestedBy = this.client.users.resolve(options.requestedBy);
if (!("searchEngine" in options))
options.searchEngine = types_1.QueryType.AUTO;
if (typeof options.searchEngine === "string" && this.extractors.has(options.searchEngine)) {
const extractor = this.extractors.get(options.searchEngine);
if (!extractor.validate(query))
return { playlist: null, tracks: [] };
const data = await extractor.handle(query);
if (data && data.data.length) {
const playlist = !data.playlist
? null
: new Playlist_1.Playlist(this, {
...data.playlist,
tracks: []
});
const tracks = data.data.map((m) => new Track_1.default(this, {
...m,
requestedBy: options.requestedBy,
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.duration)),
playlist: playlist
}));
if (playlist)
playlist.tracks = tracks;
return { playlist: playlist, tracks: tracks };
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, extractor] of this.extractors) {
if (options.blockExtractor)
break;
if (!extractor.validate(query))
continue;
const data = await extractor.handle(query);
if (data && data.data.length) {
const playlist = !data.playlist
? null
: new Playlist_1.Playlist(this, {
...data.playlist,
tracks: []
});
const tracks = data.data.map((m) => new Track_1.default(this, {
...m,
requestedBy: options.requestedBy,
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.duration)),
playlist: playlist
}));
if (playlist)
playlist.tracks = tracks;
return { playlist: playlist, tracks: tracks };
}
}
const qt = options.searchEngine === types_1.QueryType.AUTO ? await QueryResolver_1.QueryResolver.resolve(query) : options.searchEngine;
switch (qt) {
case types_1.QueryType.YOUTUBE_VIDEO: {
const info = await play_dl_1.default.video_info(query).catch(Util_1.Util.noop);
if (!info)
return { playlist: null, tracks: [] };
const track = new Track_1.default(this, {
title: info.video_details.title,
description: info.video_details.description,
author: info.video_details.channel?.name,
url: info.video_details.url,
requestedBy: options.requestedBy,
thumbnail: Util_1.Util.last(info.video_details.thumbnails)?.url,
views: info.video_details.views || 0,
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(info.video_details.durationInSec * 1000)),
source: "youtube",
raw: info
});
return { playlist: null, tracks: [track] };
}
case types_1.QueryType.YOUTUBE_SEARCH: {
const videos = await play_dl_1.default.search(query, {
limit: 10,
source: { youtube: "video" }
}).catch(Util_1.Util.noop);
if (!videos)
return { playlist: null, tracks: [] };
const tracks = videos.map(m => {
m.source = "youtube"; // eslint-disable-line @typescript-eslint/no-explicit-any
return new Track_1.default(this, {
title: m.title,
description: m.description,
author: m.channel?.name,
url: m.url,
requestedBy: options.requestedBy,
thumbnail: Util_1.Util.last(m.thumbnails).url,
views: m.views,
duration: m.durationRaw,
source: "youtube",
raw: m
});
});
return { playlist: null, tracks, searched: true };
}
case types_1.QueryType.SOUNDCLOUD_TRACK:
case types_1.QueryType.SOUNDCLOUD_SEARCH: {
const result = await QueryResolver_1.QueryResolver.resolve(query) === types_1.QueryType.SOUNDCLOUD_TRACK ? [{ url: query }] : await play_dl_1.default.search(query, {
limit: 5,
source: { soundcloud: "tracks" }
}).catch(() => []);
if (!result || !result.length)
return { playlist: null, tracks: [] };
const res = [];
for (const r of result) {
const trackInfo = await play_dl_1.default.soundcloud(r.url).catch(Util_1.Util.noop);
if (!trackInfo)
continue;
const track = new Track_1.default(this, {
title: trackInfo.name,
url: trackInfo.url,
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(trackInfo.durationInMs)),
description: "",
thumbnail: trackInfo.user.thumbnail,
views: 0,
author: trackInfo.user.name,
requestedBy: options.requestedBy,
source: "soundcloud",
engine: trackInfo
});
res.push(track);
}
return { playlist: null, tracks: res };
}
case types_1.QueryType.SPOTIFY_SONG: {
const spotifyData = await (0, spotify_url_info_1.default)(await Util_1.Util.getFetch())
.getData(query)
.catch(Util_1.Util.noop);
if (!spotifyData)
return { playlist: null, tracks: [] };
const spotifyTrack = new Track_1.default(this, {
title: spotifyData.name,
description: spotifyData.description ?? "",
author: spotifyData.artists[0]?.name ?? "Unknown Artist",
url: spotifyData.external_urls?.spotify ?? query,
thumbnail: spotifyData.album?.images[0]?.url ?? spotifyData.preview_url?.length
? `https://i.scdn.co/image/${spotifyData.preview_url?.split("?cid=")[1]}`
: "https://www.scdn.co/i/_global/twitter_card-default.jpg",
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(spotifyData.duration_ms)),
views: 0,
requestedBy: options.requestedBy,
source: "spotify"
});
return { playlist: null, tracks: [spotifyTrack] };
}
case types_1.QueryType.SPOTIFY_PLAYLIST:
case types_1.QueryType.SPOTIFY_ALBUM: {
const spotifyPlaylist = await (0, spotify_url_info_1.default)(await Util_1.Util.getFetch())
.getData(query)
.catch(Util_1.Util.noop);
if (!spotifyPlaylist)
return { playlist: null, tracks: [] };
const playlist = new Playlist_1.Playlist(this, {
title: spotifyPlaylist.name ?? spotifyPlaylist.title,
description: spotifyPlaylist.description ?? "",
thumbnail: spotifyPlaylist.images[0]?.url ?? "https://www.scdn.co/i/_global/twitter_card-default.jpg",
type: spotifyPlaylist.type,
source: "spotify",
author: spotifyPlaylist.type !== "playlist"
? {
name: spotifyPlaylist.artists[0]?.name ?? "Unknown Artist",
url: spotifyPlaylist.artists[0]?.external_urls?.spotify ?? null
}
: {
name: spotifyPlaylist.owner?.display_name ?? spotifyPlaylist.owner?.id ?? "Unknown Artist",
url: spotifyPlaylist.owner?.external_urls?.spotify ?? null
},
tracks: [],
id: spotifyPlaylist.id,
url: spotifyPlaylist.external_urls?.spotify ?? query,
rawPlaylist: spotifyPlaylist
});
if (spotifyPlaylist.type !== "playlist") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
playlist.tracks = spotifyPlaylist.tracks.items.map((m) => {
const data = new Track_1.default(this, {
title: m.name ?? "",
description: m.description ?? "",
author: m.artists[0]?.name ?? "Unknown Artist",
url: m.external_urls?.spotify ?? query,
thumbnail: spotifyPlaylist.images[0]?.url ?? "https://www.scdn.co/i/_global/twitter_card-default.jpg",
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.duration_ms)),
views: 0,
requestedBy: options.requestedBy,
playlist,
source: "spotify"
});
return data;
});
}
else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
playlist.tracks = spotifyPlaylist.tracks.items.map((m) => {
const data = new Track_1.default(this, {
title: m.track.name ?? "",
description: m.track.description ?? "",
author: m.track.artists?.[0]?.name ?? "Unknown Artist",
url: m.track.external_urls?.spotify ?? query,
thumbnail: m.track.album?.images?.[0]?.url ?? "https://www.scdn.co/i/_global/twitter_card-default.jpg",
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(m.track.duration_ms)),
views: 0,
requestedBy: options.requestedBy,
playlist,
source: "spotify"
});
return data;
});
}
return { playlist: playlist, tracks: playlist.tracks };
}
case types_1.QueryType.SOUNDCLOUD_PLAYLIST: {
const data = await play_dl_1.default.soundcloud(query).catch(Util_1.Util.noop);
if (!data)
return { playlist: null, tracks: [] };
const res = new Playlist_1.Playlist(this, {
title: data.name,
description: "",
thumbnail: "https://soundcloud.com/pwa-icon-192.png",
type: "playlist",
source: "soundcloud",
author: {
name: data.user.name ?? "Unknown Owner",
url: data.user.url
},
tracks: [],
id: `${data.id}`,
url: data.url,
rawPlaylist: data
});
const songs = await data.all_tracks();
for (const song of songs) {
const track = new Track_1.default(this, {
title: song.name,
description: "",
author: song.publisher.name ?? "Unknown Publisher",
url: song.url,
thumbnail: song.thumbnail,
duration: Util_1.Util.buildTimeCode(Util_1.Util.parseMS(song.durationInMs)),
views: 0,
requestedBy: options.requestedBy,
playlist: res,
source: "soundcloud",
engine: song
});
res.tracks.push(track);
}
return { playlist: res, tracks: res.tracks };
}
case types_1.QueryType.YOUTUBE_PLAYLIST: {
const ytpl = await play_dl_1.default.playlist_info(query, { incomplete: true }).catch(Util_1.Util.noop);
if (!ytpl)
return { playlist: null, tracks: [] };
const playlist = new Playlist_1.Playlist(this, {
title: ytpl.title,
thumbnail: ytpl.thumbnail,
description: "",
type: "playlist",
source: "youtube",
author: {
name: ytpl.channel.name,
url: ytpl.channel.url
},
tracks: [],
id: ytpl.id,
url: ytpl.url,
rawPlaylist: ytpl
});
const videos = await ytpl.all_videos();
playlist.tracks = videos.map(video => new Track_1.default(this, {
title: video.title,
description: video.description,
author: video.channel?.name,
url: video.url,
requestedBy: options.requestedBy,
thumbnail: Util_1.Util.last(video.thumbnails).url,
views: video.views,
duration: video.durationRaw,
raw: video,
playlist: playlist,
source: "youtube"
}));
return { playlist: playlist, tracks: playlist.tracks };
}
default:
return { playlist: null, tracks: [] };
}
}
/**
* Registers extractor
* @param {string} extractorName The extractor name
* @param {ExtractorModel|any} extractor The extractor object
* @param {boolean} [force=false] Overwrite existing extractor with this name (if available)
* @returns {ExtractorModel}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
use(extractorName, extractor, force = false) {
if (!extractorName)
throw new PlayerError_1.PlayerError("Cannot use unknown extractor!", PlayerError_1.ErrorStatusCode.UNKNOWN_EXTRACTOR);
if (this.extractors.has(extractorName) && !force)
return this.extractors.get(extractorName);
if (extractor instanceof ExtractorModel_1.ExtractorModel) {
this.extractors.set(extractorName, extractor);
return extractor;
}
for (const method of ["validate", "getInfo"]) {
if (typeof extractor[method] !== "function")
throw new PlayerError_1.PlayerError("Invalid extractor data!", PlayerError_1.ErrorStatusCode.INVALID_EXTRACTOR);
}
const model = new ExtractorModel_1.ExtractorModel(extractorName, extractor);
this.extractors.set(model.name, model);
return model;
}
/**
* Removes registered extractor
* @param {string} extractorName The extractor name
* @returns {ExtractorModel}
*/
unuse(extractorName) {
if (!this.extractors.has(extractorName))
throw new PlayerError_1.PlayerError(`Cannot find extractor "${extractorName}"`, PlayerError_1.ErrorStatusCode.UNKNOWN_EXTRACTOR);
const prev = this.extractors.get(extractorName);
this.extractors.delete(extractorName);
return prev;
}
/**
* Generates a report of the dependencies used by the `@discordjs/voice` module. Useful for debugging.
* @returns {string}
*/
scanDeps() {
const line = "-".repeat(50);
const depsReport = (0, voice_1.generateDependencyReport)();
const extractorReport = this.extractors
.map((m) => {
return `${m.name} :: ${m.version || "0.1.0"}`;
})
.join("\n");
return `${depsReport}\n${line}\nLoaded Extractors:\n${extractorReport || "None"}`;
}
emit(eventName, ...args) {
if (this.requiredEvents.includes(eventName) && !super.eventNames().includes(eventName)) {
// eslint-disable-next-line no-console
console.error(...args);
process.emitWarning(`[DiscordPlayerWarning] Unhandled "${eventName}" event! Events ${this.requiredEvents.map((m) => `"${m}"`).join(", ")} must have event listeners!`);
return false;
}
else {
return super.emit(eventName, ...args);
}
}
/**
* Resolves queue
* @param {GuildResolvable|Queue} queueLike Queue like object
* @returns {Queue}
*/
resolveQueue(queueLike) {
return this.getQueue(queueLike instanceof Queue_1.Queue ? queueLike.guild : queueLike);
}
*[Symbol.iterator]() {
yield* Array.from(this.queues.values());
}
/**
* Creates `Playlist` instance
* @param data The data to initialize a playlist
*/
createPlaylist(data) {
return new Playlist_1.Playlist(this, data);
}
}
exports.Player = Player;

View file

@ -1,29 +0,0 @@
import { ExtractorModelData } from "../types/types";
declare class ExtractorModel {
name: string;
private _raw;
/**
* Model for raw Discord Player extractors
* @param {string} extractorName Name of the extractor
* @param {object} data Extractor object
*/
constructor(extractorName: string, data: any);
/**
* Method to handle requests from `Player.play()`
* @param {string} query Query to handle
* @returns {Promise<ExtractorModelData>}
*/
handle(query: string): Promise<ExtractorModelData>;
/**
* Method used by Discord Player to validate query with this extractor
* @param {string} query The query to validate
* @returns {boolean}
*/
validate(query: string): boolean;
/**
* The extractor version
* @type {string}
*/
get version(): string;
}
export { ExtractorModel };

View file

@ -1,65 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtractorModel = void 0;
class ExtractorModel {
/**
* Model for raw Discord Player extractors
* @param {string} extractorName Name of the extractor
* @param {object} data Extractor object
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(extractorName, data) {
/**
* The extractor name
* @type {string}
*/
this.name = extractorName;
/**
* The raw model
* @name ExtractorModel#_raw
* @type {any}
* @private
*/
Object.defineProperty(this, "_raw", { value: data, configurable: false, writable: false, enumerable: false });
}
/**
* Method to handle requests from `Player.play()`
* @param {string} query Query to handle
* @returns {Promise<ExtractorModelData>}
*/
async handle(query) {
const data = await this._raw.getInfo(query);
if (!data)
return null;
return {
playlist: data.playlist ?? null,
data: data.info?.map((m) => ({
title: m.title,
duration: m.duration,
thumbnail: m.thumbnail,
engine: m.engine,
views: m.views,
author: m.author,
description: m.description,
url: m.url,
source: m.source || "arbitrary"
})) ?? []
};
}
/**
* Method used by Discord Player to validate query with this extractor
* @param {string} query The query to validate
* @returns {boolean}
*/
validate(query) {
return Boolean(this._raw.validate(query));
}
/**
* The extractor version
* @type {string}
*/
get version() {
return this._raw.version ?? "0.0.0";
}
}
exports.ExtractorModel = ExtractorModel;

View file

@ -1,31 +0,0 @@
export declare enum ErrorStatusCode {
STREAM_ERROR = "StreamError",
AUDIO_PLAYER_ERROR = "AudioPlayerError",
PLAYER_ERROR = "PlayerError",
NO_AUDIO_RESOURCE = "NoAudioResource",
UNKNOWN_GUILD = "UnknownGuild",
INVALID_ARG_TYPE = "InvalidArgType",
UNKNOWN_EXTRACTOR = "UnknownExtractor",
INVALID_EXTRACTOR = "InvalidExtractor",
INVALID_CHANNEL_TYPE = "InvalidChannelType",
INVALID_TRACK = "InvalidTrack",
UNKNOWN_REPEAT_MODE = "UnknownRepeatMode",
TRACK_NOT_FOUND = "TrackNotFound",
NO_CONNECTION = "NoConnection",
DESTROYED_QUEUE = "DestroyedQueue"
}
export declare class PlayerError extends Error {
message: string;
statusCode: ErrorStatusCode;
createdAt: Date;
constructor(message: string, code?: ErrorStatusCode);
get createdTimestamp(): number;
valueOf(): ErrorStatusCode;
toJSON(): {
stack: string;
code: ErrorStatusCode;
message: string;
created: number;
};
toString(): string;
}

View file

@ -1,48 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlayerError = exports.ErrorStatusCode = void 0;
var ErrorStatusCode;
(function (ErrorStatusCode) {
ErrorStatusCode["STREAM_ERROR"] = "StreamError";
ErrorStatusCode["AUDIO_PLAYER_ERROR"] = "AudioPlayerError";
ErrorStatusCode["PLAYER_ERROR"] = "PlayerError";
ErrorStatusCode["NO_AUDIO_RESOURCE"] = "NoAudioResource";
ErrorStatusCode["UNKNOWN_GUILD"] = "UnknownGuild";
ErrorStatusCode["INVALID_ARG_TYPE"] = "InvalidArgType";
ErrorStatusCode["UNKNOWN_EXTRACTOR"] = "UnknownExtractor";
ErrorStatusCode["INVALID_EXTRACTOR"] = "InvalidExtractor";
ErrorStatusCode["INVALID_CHANNEL_TYPE"] = "InvalidChannelType";
ErrorStatusCode["INVALID_TRACK"] = "InvalidTrack";
ErrorStatusCode["UNKNOWN_REPEAT_MODE"] = "UnknownRepeatMode";
ErrorStatusCode["TRACK_NOT_FOUND"] = "TrackNotFound";
ErrorStatusCode["NO_CONNECTION"] = "NoConnection";
ErrorStatusCode["DESTROYED_QUEUE"] = "DestroyedQueue";
})(ErrorStatusCode = exports.ErrorStatusCode || (exports.ErrorStatusCode = {}));
class PlayerError extends Error {
constructor(message, code = ErrorStatusCode.PLAYER_ERROR) {
super();
this.createdAt = new Date();
this.message = `[${code}] ${message}`;
this.statusCode = code;
this.name = code;
Error.captureStackTrace(this);
}
get createdTimestamp() {
return this.createdAt.getTime();
}
valueOf() {
return this.statusCode;
}
toJSON() {
return {
stack: this.stack,
code: this.statusCode,
message: this.message,
created: this.createdTimestamp
};
}
toString() {
return this.stack;
}
}
exports.PlayerError = PlayerError;

View file

@ -1,33 +0,0 @@
import { Player } from "../Player";
import { Track } from "./Track";
import { PlaylistInitData, PlaylistJSON, TrackSource } from "../types/types";
declare class Playlist {
readonly player: Player;
tracks: Track[];
title: string;
description: string;
thumbnail: string;
type: "album" | "playlist";
source: TrackSource;
author: {
name: string;
url: string;
};
id: string;
url: string;
readonly rawPlaylist?: any;
/**
* Playlist constructor
* @param {Player} player The player
* @param {PlaylistInitData} data The data
*/
constructor(player: Player, data: PlaylistInitData);
[Symbol.iterator](): Generator<Track, void, undefined>;
/**
* JSON representation of this playlist
* @param {boolean} [withTracks=true] If it should build json with tracks
* @returns {PlaylistJSON}
*/
toJSON(withTracks?: boolean): PlaylistJSON;
}
export { Playlist };

View file

@ -1,108 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Playlist = void 0;
class Playlist {
/**
* Playlist constructor
* @param {Player} player The player
* @param {PlaylistInitData} data The data
*/
constructor(player, data) {
/**
* The player
* @name Playlist#player
* @type {Player}
* @readonly
*/
this.player = player;
/**
* The tracks in this playlist
* @name Playlist#tracks
* @type {Track[]}
*/
this.tracks = data.tracks ?? [];
/**
* The author of this playlist
* @name Playlist#author
* @type {object}
*/
this.author = data.author;
/**
* The description
* @name Playlist#description
* @type {string}
*/
this.description = data.description;
/**
* The thumbnail of this playlist
* @name Playlist#thumbnail
* @type {string}
*/
this.thumbnail = data.thumbnail;
/**
* The playlist type:
* - `album`
* - `playlist`
* @name Playlist#type
* @type {string}
*/
this.type = data.type;
/**
* The source of this playlist:
* - `youtube`
* - `soundcloud`
* - `spotify`
* - `arbitrary`
* @name Playlist#source
* @type {string}
*/
this.source = data.source;
/**
* The playlist id
* @name Playlist#id
* @type {string}
*/
this.id = data.id;
/**
* The playlist url
* @name Playlist#url
* @type {string}
*/
this.url = data.url;
/**
* The playlist title
* @type {string}
*/
this.title = data.title;
/**
* @name Playlist#rawPlaylist
* @type {any}
* @readonly
*/
}
*[Symbol.iterator]() {
yield* this.tracks;
}
/**
* JSON representation of this playlist
* @param {boolean} [withTracks=true] If it should build json with tracks
* @returns {PlaylistJSON}
*/
toJSON(withTracks = true) {
const payload = {
id: this.id,
url: this.url,
title: this.title,
description: this.description,
thumbnail: this.thumbnail,
type: this.type,
source: this.source,
author: this.author,
tracks: []
};
if (withTracks)
payload.tracks = this.tracks.map((m) => m.toJSON(true));
return payload;
}
}
exports.Playlist = Playlist;

View file

@ -1,241 +0,0 @@
/// <reference types="node" />
/// <reference types="node" />
import { Collection, Guild, GuildChannelResolvable } from "discord.js";
import { Player } from "../Player";
import { StreamDispatcher } from "../VoiceInterface/StreamDispatcher";
import Track from "./Track";
import { PlayerOptions, PlayerProgressbarOptions, PlayOptions, QueueFilters, QueueRepeatMode, TrackSource } from "../types/types";
import type { Readable } from "stream";
declare class Queue<T = unknown> {
#private;
readonly guild: Guild;
readonly player: Player;
connection: StreamDispatcher;
tracks: Track[];
previousTracks: Track[];
options: PlayerOptions;
playing: boolean;
metadata?: T;
repeatMode: QueueRepeatMode;
readonly id: string;
private _streamTime;
_cooldownsTimeout: Collection<string, NodeJS.Timeout>;
private _activeFilters;
private _filtersUpdate;
onBeforeCreateStream: (track: Track, source: TrackSource, queue: Queue) => Promise<Readable | undefined>;
/**
* Queue constructor
* @param {Player} player The player that instantiated this queue
* @param {Guild} guild The guild that instantiated this queue
* @param {PlayerOptions} [options] Player options for the queue
*/
constructor(player: Player, guild: Guild, options?: PlayerOptions);
/**
* Returns current track
* @type {Track}
*/
get current(): Track;
/**
* If this queue is destroyed
* @type {boolean}
*/
get destroyed(): boolean;
/**
* Returns current track
* @returns {Track}
*/
nowPlaying(): Track;
/**
* Connects to a voice channel
* @param {GuildChannelResolvable} channel The voice/stage channel
* @returns {Promise<Queue>}
*/
connect(channel: GuildChannelResolvable): Promise<this>;
/**
* Destroys this queue
* @param {boolean} [disconnect=this.options.leaveOnStop] If it should leave on destroy
* @returns {void}
*/
destroy(disconnect?: boolean): void;
/**
* Skips current track
* @returns {boolean}
*/
skip(): boolean;
/**
* Adds single track to the queue
* @param {Track} track The track to add
* @returns {void}
*/
addTrack(track: Track): void;
/**
* Adds multiple tracks to the queue
* @param {Track[]} tracks Array of tracks to add
*/
addTracks(tracks: Track[]): void;
/**
* Sets paused state
* @param {boolean} paused The paused state
* @returns {boolean}
*/
setPaused(paused?: boolean): boolean;
/**
* Sets bitrate
* @param {number|auto} bitrate bitrate to set
* @returns {void}
*/
setBitrate(bitrate: number | "auto"): void;
/**
* Sets volume
* @param {number} amount The volume amount
* @returns {boolean}
*/
setVolume(amount: number): boolean;
/**
* Sets repeat mode
* @param {QueueRepeatMode} mode The repeat mode
* @returns {boolean}
*/
setRepeatMode(mode: QueueRepeatMode): boolean;
/**
* The current volume amount
* @type {number}
*/
get volume(): number;
set volume(amount: number);
/**
* The stream time of this queue
* @type {number}
*/
get streamTime(): number;
set streamTime(time: number);
/**
* Returns enabled filters
* @returns {AudioFilters}
*/
getFiltersEnabled(): (keyof QueueFilters)[];
/**
* Returns disabled filters
* @returns {AudioFilters}
*/
getFiltersDisabled(): (keyof QueueFilters)[];
/**
* Sets filters
* @param {QueueFilters} filters Queue filters
* @returns {Promise<void>}
*/
setFilters(filters?: QueueFilters): Promise<void>;
/**
* Seeks to the given time
* @param {number} position The position
* @returns {boolean}
*/
seek(position: number): Promise<boolean>;
/**
* Plays previous track
* @returns {Promise<void>}
*/
back(): Promise<void>;
/**
* Clear this queue
*/
clear(): void;
/**
* Stops the player
* @returns {void}
*/
stop(): void;
/**
* Shuffles this queue
* @returns {boolean}
*/
shuffle(): boolean;
/**
* Removes a track from the queue
* @param {Track|string|number} track The track to remove
* @returns {Track}
*/
remove(track: Track | string | number): Track;
/**
* Returns the index of the specified track. If found, returns the track index else returns -1.
* @param {number|Track|string} track The track
* @returns {number}
*/
getTrackPosition(track: number | Track | string): number;
/**
* Jumps to particular track
* @param {Track|number} track The track
* @returns {void}
*/
jump(track: Track | number): void;
/**
* Jumps to particular track, removing other tracks on the way
* @param {Track|number} track The track
* @returns {void}
*/
skipTo(track: Track | number): void;
/**
* Inserts the given track to specified index
* @param {Track} track The track to insert
* @param {number} [index=0] The index where this track should be
*/
insert(track: Track, index?: number): void;
/**
* @typedef {object} PlayerTimestamp
* @property {string} current The current progress
* @property {string} end The total time
* @property {number} progress Progress in %
*/
/**
* Returns player stream timestamp
* @returns {PlayerTimestamp}
*/
getPlayerTimestamp(): {
current: string;
end: string;
progress: number;
};
/**
* Creates progress bar string
* @param {PlayerProgressbarOptions} options The progress bar options
* @returns {string}
*/
createProgressBar(options?: PlayerProgressbarOptions): string;
/**
* Total duration
* @type {Number}
*/
get totalTime(): number;
/**
* Play stream in a voice/stage channel
* @param {Track} [src] The track to play (if empty, uses first track from the queue)
* @param {PlayOptions} [options] The options
* @returns {Promise<void>}
*/
play(src?: Track, options?: PlayOptions): Promise<void>;
/**
* Private method to handle autoplay
* @param {Track} track The source track to find its similar track for autoplay
* @returns {Promise<void>}
* @private
*/
private _handleAutoplay;
[Symbol.iterator](): Generator<Track, void, undefined>;
/**
* JSON representation of this queue
* @returns {object}
*/
toJSON(): {
id: string;
guild: string;
voiceChannel: string;
options: PlayerOptions;
tracks: import("../types/types").TrackJSON[];
};
/**
* String representation of this queue
* @returns {string}
*/
toString(): string;
}
export { Queue };

View file

@ -1,761 +0,0 @@
"use strict";
var _Queue_instances, _Queue_lastVolume, _Queue_destroyed, _Queue_watchDestroyed, _Queue_getBufferingTimeout;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Queue = void 0;
const tslib_1 = require("tslib");
const discord_js_1 = require("discord.js");
const Track_1 = tslib_1.__importDefault(require("./Track"));
const types_1 = require("../types/types");
const voice_1 = require("@discordjs/voice");
const play_dl_1 = tslib_1.__importDefault(require("play-dl"));
const Util_1 = require("../utils/Util");
const AudioFilters_1 = tslib_1.__importDefault(require("../utils/AudioFilters"));
const PlayerError_1 = require("./PlayerError");
const FFmpegStream_1 = require("../utils/FFmpegStream");
class Queue {
/**
* Queue constructor
* @param {Player} player The player that instantiated this queue
* @param {Guild} guild The guild that instantiated this queue
* @param {PlayerOptions} [options] Player options for the queue
*/
constructor(player, guild, options = {}) {
_Queue_instances.add(this);
this.tracks = [];
this.previousTracks = [];
this.playing = false;
this.metadata = null;
this.repeatMode = 0;
this.id = discord_js_1.SnowflakeUtil.generate().toString();
this._streamTime = 0;
this._cooldownsTimeout = new discord_js_1.Collection();
this._activeFilters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
this._filtersUpdate = false;
_Queue_lastVolume.set(this, 0);
_Queue_destroyed.set(this, false);
this.onBeforeCreateStream = null;
/**
* The player that instantiated this queue
* @type {Player}
* @readonly
*/
this.player = player;
/**
* The guild that instantiated this queue
* @type {Guild}
* @readonly
*/
this.guild = guild;
/**
* The player options for this queue
* @type {PlayerOptions}
*/
this.options = {};
/**
* Queue repeat mode
* @type {QueueRepeatMode}
* @name Queue#repeatMode
*/
/**
* Queue metadata
* @type {any}
* @name Queue#metadata
*/
/**
* Previous tracks
* @type {Track[]}
* @name Queue#previousTracks
*/
/**
* Regular tracks
* @type {Track[]}
* @name Queue#tracks
*/
/**
* The connection
* @type {StreamDispatcher}
* @name Queue#connection
*/
/**
* The ID of this queue
* @type {Snowflake}
* @name Queue#id
*/
Object.assign(this.options, {
leaveOnEnd: true,
leaveOnStop: true,
leaveOnEmpty: true,
leaveOnEmptyCooldown: 1000,
autoSelfDeaf: true,
ytdlOptions: {
highWaterMark: 1 << 25
},
initialVolume: 100,
bufferingTimeout: 3000,
spotifyBridge: true,
disableVolume: false
}, options);
if ("onBeforeCreateStream" in this.options)
this.onBeforeCreateStream = this.options.onBeforeCreateStream;
this.player.emit("debug", this, `Queue initialized:\n\n${this.player.scanDeps()}`);
}
/**
* Returns current track
* @type {Track}
*/
get current() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return this.connection.audioResource?.metadata ?? this.tracks[0];
}
/**
* If this queue is destroyed
* @type {boolean}
*/
get destroyed() {
return tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f");
}
/**
* Returns current track
* @returns {Track}
*/
nowPlaying() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return this.current;
}
/**
* Connects to a voice channel
* @param {GuildChannelResolvable} channel The voice/stage channel
* @returns {Promise<Queue>}
*/
async connect(channel) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const _channel = this.guild.channels.resolve(channel);
if (![discord_js_1.ChannelType.GuildStageVoice, discord_js_1.ChannelType.GuildVoice].includes(_channel?.type))
throw new PlayerError_1.PlayerError(`Channel type must be GuildVoice or GuildStageVoice, got ${_channel?.type}!`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
const connection = await this.player.voiceUtils.connect(_channel, {
deaf: this.options.autoSelfDeaf
});
this.connection = connection;
if (_channel.type === discord_js_1.ChannelType.GuildStageVoice) {
await _channel.guild.members.me.voice.setSuppressed(false).catch(async () => {
return await _channel.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
});
}
this.connection.on("error", (err) => {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
return;
this.player.emit("connectionError", this, err);
});
this.connection.on("debug", (msg) => {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
return;
this.player.emit("debug", this, msg);
});
this.player.emit("connectionCreate", this, this.connection);
this.connection.on("start", (resource) => {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
return;
this.playing = true;
if (!this._filtersUpdate)
this.player.emit("trackStart", this, resource?.metadata ?? this.current);
this._filtersUpdate = false;
});
this.connection.on("finish", async (resource) => {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
return;
this.playing = false;
if (this._filtersUpdate)
return;
this._streamTime = 0;
if (resource?.metadata)
this.previousTracks.push(resource.metadata);
this.player.emit("trackEnd", this, resource.metadata);
if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.OFF) {
if (this.options.leaveOnEnd)
this.destroy();
this.player.emit("queueEnd", this);
}
else if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.AUTOPLAY) {
this._handleAutoplay(Util_1.Util.last(this.previousTracks));
}
else {
if (this.repeatMode === types_1.QueueRepeatMode.TRACK)
return void this.play(Util_1.Util.last(this.previousTracks), { immediate: true });
if (this.repeatMode === types_1.QueueRepeatMode.QUEUE)
this.tracks.push(Util_1.Util.last(this.previousTracks));
const nextTrack = this.tracks.shift();
this.play(nextTrack, { immediate: true });
return;
}
});
return this;
}
/**
* Destroys this queue
* @param {boolean} [disconnect=this.options.leaveOnStop] If it should leave on destroy
* @returns {void}
*/
destroy(disconnect = this.options.leaveOnStop) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (this.connection)
this.connection.end();
if (disconnect)
this.connection?.disconnect();
this.player.queues.delete(this.guild.id);
this.player.voiceUtils.cache.delete(this.guild.id);
tslib_1.__classPrivateFieldSet(this, _Queue_destroyed, true, "f");
}
/**
* Skips current track
* @returns {boolean}
*/
skip() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection)
return false;
this._filtersUpdate = false;
this.connection.end();
return true;
}
/**
* Adds single track to the queue
* @param {Track} track The track to add
* @returns {void}
*/
addTrack(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!(track instanceof Track_1.default))
throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
this.tracks.push(track);
this.player.emit("trackAdd", this, track);
}
/**
* Adds multiple tracks to the queue
* @param {Track[]} tracks Array of tracks to add
*/
addTracks(tracks) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!tracks.every((y) => y instanceof Track_1.default))
throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
this.tracks.push(...tracks);
this.player.emit("tracksAdd", this, tracks);
}
/**
* Sets paused state
* @param {boolean} paused The paused state
* @returns {boolean}
*/
setPaused(paused) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection)
return false;
return paused ? this.connection.pause(true) : this.connection.resume();
}
/**
* Sets bitrate
* @param {number|auto} bitrate bitrate to set
* @returns {void}
*/
setBitrate(bitrate) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection?.audioResource?.encoder)
return;
if (bitrate === "auto")
bitrate = this.connection.channel?.bitrate ?? 64000;
this.connection.audioResource.encoder.setBitrate(bitrate);
}
/**
* Sets volume
* @param {number} amount The volume amount
* @returns {boolean}
*/
setVolume(amount) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection)
return false;
tslib_1.__classPrivateFieldSet(this, _Queue_lastVolume, amount, "f");
this.options.initialVolume = amount;
return this.connection.setVolume(amount);
}
/**
* Sets repeat mode
* @param {QueueRepeatMode} mode The repeat mode
* @returns {boolean}
*/
setRepeatMode(mode) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (![types_1.QueueRepeatMode.OFF, types_1.QueueRepeatMode.QUEUE, types_1.QueueRepeatMode.TRACK, types_1.QueueRepeatMode.AUTOPLAY].includes(mode))
throw new PlayerError_1.PlayerError(`Unknown repeat mode "${mode}"!`, PlayerError_1.ErrorStatusCode.UNKNOWN_REPEAT_MODE);
if (mode === this.repeatMode)
return false;
this.repeatMode = mode;
return true;
}
/**
* The current volume amount
* @type {number}
*/
get volume() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection)
return 100;
return this.connection.volume;
}
set volume(amount) {
this.setVolume(amount);
}
/**
* The stream time of this queue
* @type {number}
*/
get streamTime() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.connection)
return 0;
const playbackTime = this._streamTime + this.connection.streamTime;
const NC = this._activeFilters.includes("nightcore") ? 1.25 : null;
const VW = this._activeFilters.includes("vaporwave") ? 0.8 : null;
if (NC && VW)
return playbackTime * (NC + VW);
return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime;
}
set streamTime(time) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
this.seek(time);
}
/**
* Returns enabled filters
* @returns {AudioFilters}
*/
getFiltersEnabled() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return AudioFilters_1.default.names.filter((x) => this._activeFilters.includes(x));
}
/**
* Returns disabled filters
* @returns {AudioFilters}
*/
getFiltersDisabled() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return AudioFilters_1.default.names.filter((x) => !this._activeFilters.includes(x));
}
/**
* Sets filters
* @param {QueueFilters} filters Queue filters
* @returns {Promise<void>}
*/
async setFilters(filters) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!filters || !Object.keys(filters).length) {
// reset filters
const streamTime = this.streamTime;
this._activeFilters = [];
return await this.play(this.current, {
immediate: true,
filtersUpdate: true,
seek: streamTime,
encoderArgs: []
});
}
const _filters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
for (const filter in filters) {
if (filters[filter] === true)
_filters.push(filter);
}
if (this._activeFilters.join("") === _filters.join(""))
return;
const newFilters = AudioFilters_1.default.create(_filters).trim();
const streamTime = this.streamTime;
this._activeFilters = _filters;
return await this.play(this.current, {
immediate: true,
filtersUpdate: true,
seek: streamTime,
encoderArgs: !_filters.length ? undefined : ["-af", newFilters]
});
}
/**
* Seeks to the given time
* @param {number} position The position
* @returns {boolean}
*/
async seek(position) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.playing || !this.current)
return false;
if (position < 1)
position = 0;
if (position >= this.current.durationMS)
return this.skip();
await this.play(this.current, {
immediate: true,
filtersUpdate: true,
seek: position
});
return true;
}
/**
* Plays previous track
* @returns {Promise<void>}
*/
async back() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const prev = this.previousTracks[this.previousTracks.length - 2]; // because last item is the current track
if (!prev)
throw new PlayerError_1.PlayerError("Could not find previous track", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
return await this.play(prev, { immediate: true });
}
/**
* Clear this queue
*/
clear() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
this.tracks = [];
this.previousTracks = [];
}
/**
* Stops the player
* @returns {void}
*/
stop() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return this.destroy();
}
/**
* Shuffles this queue
* @returns {boolean}
*/
shuffle() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.tracks.length || this.tracks.length < 2)
return false;
for (let i = this.tracks.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
}
return true;
}
/**
* Removes a track from the queue
* @param {Track|string|number} track The track to remove
* @returns {Track}
*/
remove(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
let trackFound = null;
if (typeof track === "number") {
trackFound = this.tracks[track];
if (trackFound) {
this.tracks = this.tracks.filter((t) => t.id !== trackFound.id);
}
}
else {
trackFound = this.tracks.find((s) => s.id === (track instanceof Track_1.default ? track.id : track));
if (trackFound) {
this.tracks = this.tracks.filter((s) => s.id !== trackFound.id);
}
}
return trackFound;
}
/**
* Returns the index of the specified track. If found, returns the track index else returns -1.
* @param {number|Track|string} track The track
* @returns {number}
*/
getTrackPosition(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (typeof track === "number")
return this.tracks[track] != null ? track : -1;
return this.tracks.findIndex((pred) => pred.id === (track instanceof Track_1.default ? track.id : track));
}
/**
* Jumps to particular track
* @param {Track|number} track The track
* @returns {void}
*/
jump(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const foundTrack = this.remove(track);
if (!foundTrack)
throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
this.tracks.splice(0, 0, foundTrack);
return void this.skip();
}
/**
* Jumps to particular track, removing other tracks on the way
* @param {Track|number} track The track
* @returns {void}
*/
skipTo(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const trackIndex = this.getTrackPosition(track);
const removedTrack = this.remove(track);
if (!removedTrack)
throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
this.tracks.splice(0, trackIndex, removedTrack);
return void this.skip();
}
/**
* Inserts the given track to specified index
* @param {Track} track The track to insert
* @param {number} [index=0] The index where this track should be
*/
insert(track, index = 0) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!track || !(track instanceof Track_1.default))
throw new PlayerError_1.PlayerError("track must be the instance of Track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
if (typeof index !== "number" || index < 0 || !Number.isFinite(index))
throw new PlayerError_1.PlayerError(`Invalid index "${index}"`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
this.tracks.splice(index, 0, track);
this.player.emit("trackAdd", this, track);
}
/**
* @typedef {object} PlayerTimestamp
* @property {string} current The current progress
* @property {string} end The total time
* @property {number} progress Progress in %
*/
/**
* Returns player stream timestamp
* @returns {PlayerTimestamp}
*/
getPlayerTimestamp() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const currentStreamTime = this.streamTime;
const totalTime = this.current.durationMS;
const currentTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(currentStreamTime));
const endTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(totalTime));
return {
current: currentTimecode,
end: endTimecode,
progress: Math.round((currentStreamTime / totalTime) * 100)
};
}
/**
* Creates progress bar string
* @param {PlayerProgressbarOptions} options The progress bar options
* @returns {string}
*/
createProgressBar(options = { timecodes: true }) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
const index = Math.round((this.streamTime / this.current.durationMS) * length);
const indicator = typeof options.indicator === "string" && options.indicator.length > 0 ? options.indicator : "🔘";
const line = typeof options.line === "string" && options.line.length > 0 ? options.line : "▬";
if (index >= 1 && index <= length) {
const bar = line.repeat(length - 1).split("");
bar.splice(index, 0, indicator);
if (options.timecodes) {
const timestamp = this.getPlayerTimestamp();
return `${timestamp.current}${bar.join("")}${timestamp.end}`;
}
else {
return `${bar.join("")}`;
}
}
else {
if (options.timecodes) {
const timestamp = this.getPlayerTimestamp();
return `${timestamp.current}${indicator}${line.repeat(length - 1)}${timestamp.end}`;
}
else {
return `${indicator}${line.repeat(length - 1)}`;
}
}
}
/**
* Total duration
* @type {Number}
*/
get totalTime() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
}
/**
* Play stream in a voice/stage channel
* @param {Track} [src] The track to play (if empty, uses first track from the queue)
* @param {PlayOptions} [options] The options
* @returns {Promise<void>}
*/
async play(src, options = {}) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
return;
if (!this.connection || !this.connection.voiceConnection)
throw new PlayerError_1.PlayerError("Voice connection is not available, use <Queue>.connect()!", PlayerError_1.ErrorStatusCode.NO_CONNECTION);
if (src && (this.playing || this.tracks.length) && !options.immediate)
return this.addTrack(src);
const track = options.filtersUpdate && !options.immediate ? src || this.current : src ?? this.tracks.shift();
if (!track)
return;
this.player.emit("debug", this, "Received play request");
if (!options.filtersUpdate) {
this.previousTracks = this.previousTracks.filter((x) => x.id !== track.id);
this.previousTracks.push(track);
}
let stream = null;
const hasCustomDownloader = typeof this.onBeforeCreateStream === "function";
if (["youtube", "spotify"].includes(track.raw.source)) {
let spotifyResolved = false;
if (this.options.spotifyBridge && track.raw.source === "spotify" && !track.raw.engine) {
track.raw.engine = await play_dl_1.default.search(`${track.author} ${track.title}`, { source: { youtube: "video" } })
.then(res => res[0].url)
.catch(() => null);
spotifyResolved = true;
}
const url = track.raw.source === "spotify" ? track.raw.engine : track.url;
if (!url)
return void this.play(this.tracks.shift(), { immediate: true });
if (hasCustomDownloader) {
stream = (await this.onBeforeCreateStream(track, spotifyResolved ? "youtube" : track.raw.source, this)) || null;
}
if (!stream) {
stream = (await play_dl_1.default.stream(url, { discordPlayerCompatibility: true })).stream;
}
}
else {
const arbitraryStream = (hasCustomDownloader && (await this.onBeforeCreateStream(track, track.raw.source || track.raw.engine, this))) || null;
stream =
arbitraryStream || (track.raw.source === "soundcloud" && typeof track.raw.engine?.downloadProgressive === "function")
? await track.raw.engine.downloadProgressive()
: typeof track.raw.engine === "function"
? await track.raw.engine()
: track.raw.engine;
}
const ffmpegStream = (0, FFmpegStream_1.createFFmpegStream)(stream, {
encoderArgs: options.encoderArgs || [],
seek: options.seek ? options.seek / 1000 : 0,
fmt: "s16le"
}).on("error", (err) => {
if (!`${err}`.toLowerCase().includes("premature close"))
this.player.emit("error", this, err);
});
const resource = this.connection.createStream(ffmpegStream, {
type: voice_1.StreamType.Raw,
data: track,
disableVolume: Boolean(this.options.disableVolume)
});
if (options.seek)
this._streamTime = options.seek;
this._filtersUpdate = options.filtersUpdate;
const volumeTransformer = resource.volume;
if (volumeTransformer && typeof this.options.initialVolume === "number")
Reflect.set(volumeTransformer, "volume", Math.pow(this.options.initialVolume / 100, 1.660964));
if (volumeTransformer?.hasSmoothness && typeof this.options.volumeSmoothness === "number") {
if (typeof volumeTransformer.setSmoothness === "function")
volumeTransformer.setSmoothness(this.options.volumeSmoothness || 0);
}
setTimeout(() => {
this.connection.playStream(resource);
}, tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_getBufferingTimeout).call(this)).unref();
}
/**
* Private method to handle autoplay
* @param {Track} track The source track to find its similar track for autoplay
* @returns {Promise<void>}
* @private
*/
async _handleAutoplay(track) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!track || ![track.source, track.raw?.source].includes("youtube")) {
if (this.options.leaveOnEnd)
this.destroy();
return void this.player.emit("queueEnd", this);
}
const info = await play_dl_1.default.video_info(track.url)
.catch(Util_1.Util.noop);
if (!info) {
if (this.options.leaveOnEnd)
this.destroy();
return void this.player.emit("queueEnd", this);
}
const randomRelated = await play_dl_1.default.video_info(info.related_videos[0]);
const nextTrack = new Track_1.default(this.player, {
title: randomRelated.video_details.title,
url: randomRelated.video_details.url,
duration: randomRelated.video_details.durationRaw ? Util_1.Util.buildTimeCode(Util_1.Util.parseMS(randomRelated.video_details.durationInSec * 1000)) : "0:00",
description: "",
thumbnail: Util_1.Util.last(randomRelated.video_details.thumbnails).url,
views: randomRelated.video_details.views,
author: randomRelated.video_details.channel.name,
requestedBy: track.requestedBy,
source: "youtube"
});
this.play(nextTrack, { immediate: true });
}
*[(_Queue_lastVolume = new WeakMap(), _Queue_destroyed = new WeakMap(), _Queue_instances = new WeakSet(), Symbol.iterator)]() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
yield* this.tracks;
}
/**
* JSON representation of this queue
* @returns {object}
*/
toJSON() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
return {
id: this.id,
guild: this.guild.id,
voiceChannel: this.connection?.channel?.id,
options: this.options,
tracks: this.tracks.map((m) => m.toJSON())
};
}
/**
* String representation of this queue
* @returns {string}
*/
toString() {
if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
return;
if (!this.tracks.length)
return "No songs available to display!";
return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
}
}
exports.Queue = Queue;
_Queue_watchDestroyed = function _Queue_watchDestroyed(emit = true) {
if (tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f")) {
if (emit)
this.player.emit("error", this, new PlayerError_1.PlayerError("Cannot use destroyed queue", PlayerError_1.ErrorStatusCode.DESTROYED_QUEUE));
return true;
}
return false;
}, _Queue_getBufferingTimeout = function _Queue_getBufferingTimeout() {
const timeout = this.options.bufferingTimeout;
if (isNaN(timeout) || timeout < 0 || !Number.isFinite(timeout))
return 1000;
return timeout;
};

View file

@ -1,53 +0,0 @@
import { User } from "discord.js";
import { Player } from "../Player";
import { RawTrackData, TrackJSON } from "../types/types";
import { Playlist } from "./Playlist";
import { Queue } from "./Queue";
declare class Track {
player: Player;
title: string;
description: string;
author: string;
url: string;
thumbnail: string;
duration: string;
views: number;
requestedBy: User;
playlist?: Playlist;
readonly raw: RawTrackData;
readonly id: string;
/**
* Track constructor
* @param {Player} player The player that instantiated this Track
* @param {RawTrackData} data Track data
*/
constructor(player: Player, data: RawTrackData);
private _patch;
/**
* The queue in which this track is located
* @type {Queue}
*/
get queue(): Queue;
/**
* The track duration in millisecond
* @type {number}
*/
get durationMS(): number;
/**
* Returns source of this track
* @type {TrackSource}
*/
get source(): import("../types/types").TrackSource;
/**
* String representation of this track
* @returns {string}
*/
toString(): string;
/**
* Raw JSON representation of this track
* @returns {TrackJSON}
*/
toJSON(hidePlaylist?: boolean): TrackJSON;
}
export default Track;
export { Track };

Some files were not shown because too many files have changed in this diff Show more