v4.4.0 - https:/dash.jababot.ru/updates

This commit is contained in:
Jonny_Bro (Nikita) 2023-11-04 16:56:10 +05:00
parent 82a92f0caa
commit a9edce1b75
30 changed files with 580 additions and 245 deletions

View file

@ -38,14 +38,21 @@ class JaBa extends Client {
this.player.extractors.loadDefault();
this.player.events.on("playerStart", async (queue, track) => {
const m = await queue.metadata.channel.send({ content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language) });
const m = (await queue.metadata.channel.send({
content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language),
})).id;
if (track.durationMS > 1)
setTimeout(() => {
if (m.deletable) m.delete();
const message = queue.metadata.channel.messages.cache.get(m);
if (message.deletable) message.delete();
}, track.durationMS);
else
setTimeout(() => {
if (m.deletable) m.delete();
const message = queue.metadata.channel.messages.cache.get(m);
if (message.deletable) message.delete();
}, 5 * 60 * 1000); // m * s * ms
});
this.player.events.on("emptyQueue", queue => queue.metadata.channel.send(this.translate("music/play:QUEUE_ENDED", null, queue.metadata.channel.guild.data.language)));
@ -227,10 +234,9 @@ class JaBa extends Client {
* @param {String} locale Language
*/
translate(key, args, locale = this.defaultLanguage) {
const language = this.translations.get(locale);
if (!language) throw "Invalid language set in data.";
const lang = this.translations.get(locale);
return language(key, args);
return lang(key, args);
}
/**

View file

@ -46,7 +46,7 @@ class ImportMee6 extends BaseCommand {
content: interaction.translate("owner/debug:SUCCESS_LEVEL", {
user: interaction.member.toString(),
amount: level,
}),
}, "success"),
});
}
}

View file

@ -68,10 +68,9 @@ class Memes extends BaseCommand {
),
);
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("memes_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(tags));
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("memes_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(tags));
await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
components: [row],
});
}

View file

@ -207,7 +207,7 @@ async function changeSetting(interaction, setting, state, channel, guildData) {
await guildData.save();
return interaction.reply({
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: **${interaction.translate("common:DISABLED")}**`,
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`, null, "success")}: **${interaction.translate("common:DISABLED", null, "success")}**`,
ephemeral: true,
});
} else {
@ -226,7 +226,7 @@ async function changeSetting(interaction, setting, state, channel, guildData) {
} else
return interaction.reply({
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: ${
guildData.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${guildData.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**`
guildData.plugins[setting] ? `**${interaction.translate("common:ENABLED", null, "success")}** (<#${guildData.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED", null, "success")}**`
}`,
ephemeral: true,
});

View file

@ -102,7 +102,7 @@ class Selectroles extends BaseCommand {
}
interaction.reply({
content: interaction.translate("administration/selectroles:ROLES_UPDATED"),
content: interaction.translate("administration/selectroles:ROLES_UPDATED", null, "success"),
ephemeral: true,
});
}

View file

@ -167,18 +167,16 @@ class Marry extends BaseCommand {
content: interaction.translate("economy/marry:SUCCESS", {
creator: interaction.member.toString(),
partner: member.toString(),
}),
}, "success"),
components: [],
});
} else {
return interaction.editReply({
content: interaction.translate("economy/marry:DENIED", {
creator: interaction.member.toString(),
partner: member.toString(),
}),
components: [],
});
}
} else return interaction.editReply({
content: interaction.translate("economy/marry:DENIED", {
creator: interaction.member.toString(),
partner: member.toString(),
}, "error"),
components: [],
});
});
}
}

View file

@ -0,0 +1,46 @@
const { ContextMenuCommandBuilder, ApplicationCommandType } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class AvatarContext extends BaseCommand {
/**
*
* @param {import("../../base/JaBa")} client
*/
constructor() {
super({
command: new ContextMenuCommandBuilder()
.setName("Avatar")
.setType(ApplicationCommandType.User)
.setDMPermission(false),
aliases: [],
dirname: __dirname,
ownerOnly: false,
});
}
/**
*
* @param {import("../../base/JaBa")} client
*/
async onLoad() {
//...
}
/**
*
* @param {import("../../base/JaBa")} client
* @param {import("discord.js").UserContextMenuCommandInteraction} interaction
* @param {Object} data
*/
async execute(client, interaction) {
const avatar = interaction.targetUser.avatarURL({ size: 2048 });
interaction.reply({
files: [
{
attachment: avatar,
},
],
});
}
}
module.exports = AvatarContext;

View file

@ -29,7 +29,7 @@ class Boosters extends BaseCommand {
client.on("interactionCreate", async interaction => {
if (!interaction.isButton()) return;
if (interaction.customId.includes("boosters_")) {
if (interaction.customId.startsWith("boosters_")) {
const guildData = client.findOrCreateGuild(interaction.guildId),
boosters = (await interaction.guild.members.fetch()).filter(m => m.premiumSince),
embeds = generateBoostersEmbeds(client, interaction, boosters, guildData);
@ -41,7 +41,7 @@ class Boosters extends BaseCommand {
new ButtonBuilder().setCustomId("boosters_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
let currentPage = 0;
let currentPage = Number(interaction.message.content.match(/\d+/g)[0]) - 1 ?? 0;
if (interaction.customId === "boosters_prev_page") {
await interaction.deferUpdate();

View file

@ -108,10 +108,9 @@ class Help extends BaseCommand {
};
});
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("help_category_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(categoriesRows));
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("help_category_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(categoriesRows));
await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),
fetchReply: true,
components: [row],
});

View file

@ -1,14 +1,18 @@
const { ContextMenuCommandBuilder, ModalBuilder, EmbedBuilder, ActionRowBuilder, TextInputBuilder, ApplicationCommandType, PermissionsBitField, TextInputStyle } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Warn extends BaseCommand {
class WarnContext extends BaseCommand {
/**
*
* @param {import("../../base/JaBa")} client
*/
constructor() {
super({
command: new ContextMenuCommandBuilder().setName("warn").setType(ApplicationCommandType.User).setDMPermission(false).setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages),
command: new ContextMenuCommandBuilder()
.setName("Warn")
.setType(ApplicationCommandType.User)
.setDMPermission(false)
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages),
aliases: [],
dirname: __dirname,
ownerOnly: false,
@ -200,4 +204,4 @@ class Warn extends BaseCommand {
}
}
module.exports = Warn;
module.exports = WarnContext;

View file

@ -58,12 +58,20 @@ class Loop extends BaseCommand {
const queue = client.player.nodes.get(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true });
const translated = {
"AUTOPLAY": interaction.translate("music/loop:AUTOPLAY_ENABLED", null, "success"),
"QUEUE": interaction.translate("music/loop:QUEUE_ENABLED", null, "success"),
"TRACK": interaction.translate("music/loop:TRACK_ENABLED", null, "success"),
"OFF": interaction.translate("music/loop:LOOP_DISABLED", null, "success"),
"0": interaction.translate("music/loop:LOOP_DISABLED", null, "success"),
};
const type = interaction.options.getString("option"),
mode = type === "3" ? QueueRepeatMode.AUTOPLAY : type === "2" ? QueueRepeatMode.QUEUE : type === "1" ? QueueRepeatMode.TRACK : QueueRepeatMode.OFF;
mode = QueueRepeatMode[type];
queue.setRepeatMode(mode);
interaction.success(`music/loop:${type === "3" ? "AUTOPLAY_ENABLED" : type === "2" ? "QUEUE_ENABLED" : type === "1" ? "TRACK_ENABLED" : "LOOP_DISABLED"}`);
interaction.reply({ content: translated[type] });
}
}

View file

@ -1,4 +1,4 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"),
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } = require("discord.js"),
{ QueueRepeatMode } = require("discord-player");
const BaseCommand = require("../../base/BaseCommand");
@ -26,8 +26,156 @@ class Nowplaying extends BaseCommand {
*
* @param {import("../../base/JaBa")} client
*/
async onLoad() {
//...
async onLoad(client) {
client.on("interactionCreate", async interaction => {
if (!interaction.isButton()) return;
if (interaction.customId.startsWith("nowp_")) {
const voice = interaction.member.voice.channel;
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL");
const queue = client.player.nodes.get(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING");
const row1 = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("nowp_prev_track").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
new ButtonBuilder().setCustomId("nowp_loop").setStyle(ButtonStyle.Secondary).setEmoji("🔁"),
new ButtonBuilder().setCustomId("nowp_add_track").setStyle(ButtonStyle.Success).setEmoji("▶️"),
new ButtonBuilder().setCustomId("nowp_next_track").setStyle(ButtonStyle.Primary).setEmoji("➡️"),
);
const row2 = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("nowp_queue").setStyle(ButtonStyle.Secondary).setEmoji(""),
new ButtonBuilder().setCustomId("nowp_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
if (interaction.customId === "nowp_prev_track") {
await interaction.deferUpdate();
if (queue.history.isEmpty()) return interaction.followUp({ content: interaction.translate("music/back:NO_PREV_SONG", null, "error"), ephemeral: true });
queue.history.back();
await interaction.followUp({ content: interaction.translate("music/back:SUCCESS", null, "success"), ephemeral: true });
const embed = await updateEmbed(interaction, queue);
interaction.editReply({
embeds: [embed],
});
} else if (interaction.customId === "nowp_loop") {
await interaction.deferUpdate();
const selectMenu = new StringSelectMenuBuilder()
.setCustomId("nowp_select")
.setPlaceholder(interaction.translate("common:NOTHING_SELECTED"))
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel(interaction.translate("music/loop:AUTOPLAY"))
.setValue("3"),
new StringSelectMenuOptionBuilder()
.setLabel(interaction.translate("music/loop:QUEUE"))
.setValue("2"),
new StringSelectMenuOptionBuilder()
.setLabel(interaction.translate("music/loop:TRACK"))
.setValue("1"),
new StringSelectMenuOptionBuilder()
.setLabel(interaction.translate("music/loop:DISABLE"))
.setValue("0"),
);
const selectRow = new ActionRowBuilder().addComponents(selectMenu),
msg = await interaction.followUp({
components: [selectRow],
ephemeral: true,
});
const filter = i => i.user.id === interaction.user.id,
collected = await msg.awaitMessageComponent({ filter, time: 10 * 1000 }),
mode = QueueRepeatMode[collected.values[0]],
translated = {
"AUTOPLAY": interaction.translate("music/loop:AUTOPLAY_ENABLED", null, "success"),
"QUEUE": interaction.translate("music/loop:QUEUE_ENABLED", null, "success"),
"TRACK": interaction.translate("music/loop:TRACK_ENABLED", null, "success"),
"OFF": interaction.translate("music/loop:LOOP_DISABLED", null, "success"),
"0": interaction.translate("music/loop:LOOP_DISABLED", null, "success"),
};
await collected.deferUpdate();
queue.setRepeatMode(mode);
await interaction.followUp({ content: translated[mode] });
const embed = await updateEmbed(interaction, queue);
interaction.editReply({
embeds: [embed],
});
} else if (interaction.customId === "nowp_add_track") {
await interaction.deferUpdate();
await interaction.followUp({
content: interaction.translate("music/nowplaying:LINK"),
ephemeral: true,
});
const filter = m => m.author.id === interaction.user.id && m.content.startsWith("http"),
collected = (await interaction.channel.awaitMessages({ filter, time: 10 * 1000, max: 1 })).first(),
query = collected.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)[0],
searchResult = await client.player.search(query, {
requestedBy: interaction.user,
});
if (!searchResult.hasTracks()) return interaction.error("music/play:NO_RESULT", { query: query }, { edit: true });
else queue.addTrack(searchResult);
if (collected.deletable) collected.delete();
await interaction.followUp({
content: interaction.translate("music/play:ADDED_QUEUE", {
songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title,
}, "success"),
});
const embed = await updateEmbed(interaction, queue);
interaction.editReply({
embeds: [embed],
});
} else if (interaction.customId === "nowp_next_track") {
await interaction.deferUpdate();
queue.node.skip();
await interaction.followUp({ content: interaction.translate("music/skip:SUCCESS", null, "success"), ephemeral: true });
const embed = await updateEmbed(interaction, queue);
interaction.editReply({
embeds: [embed],
});
} else if (interaction.customId === "nowp_queue") {
await interaction.deferUpdate();
client.commands.get("queue").execute(client, interaction);
} else if (interaction.customId === "nowp_stop") {
await interaction.deferUpdate();
row1.components.forEach(component => {
component.setDisabled(true);
});
row2.components.forEach(component => {
component.setDisabled(true);
});
return interaction.editReply({
components: [row1, row2],
});
}
}
});
}
/**
*
@ -35,66 +183,92 @@ class Nowplaying extends BaseCommand {
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {Object} data
*/
async execute(client, interaction, data) {
async execute(client, interaction) {
await interaction.deferReply();
const queue = client.player.nodes.get(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true });
const progressBar = queue.node.createProgressBar(),
track = queue.currentTrack;
const embed = new EmbedBuilder()
.setAuthor({
name: interaction.translate("music/nowplaying:CURRENTLY_PLAYING"),
})
.setThumbnail(track.thumbnail)
.addFields([
{
name: interaction.translate("music/nowplaying:T_TITLE"),
value: `[${track.title}](${track.url})`,
inline: true,
},
{
name: interaction.translate("music/nowplaying:T_AUTHOR"),
value: track.author || interaction.translate("common:UNKNOWN"),
inline: true,
},
{ name: "\u200B", value: "\u200B", inline: true },
{
name: interaction.translate("common:VIEWS"),
value: track.raw.live ? "" : new Intl.NumberFormat(client.languages.find(language => language.name === data.guildData.language).moment, { notation: "standard" }).format(track.raw.views),
inline: true,
},
{
name: interaction.translate("music/queue:ADDED"),
value: track.requestedBy.toString(),
inline: true,
},
{
name: interaction.translate("music/nowplaying:T_DURATION"),
value: progressBar,
},
{
name: "\u200b",
value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${
queue.repeatMode === QueueRepeatMode.AUTOPLAY
? interaction.translate("music/nowplaying:AUTOPLAY")
: queue.repeatMode === QueueRepeatMode.QUEUE
? interaction.translate("music/nowplaying:QUEUE")
: queue.repeatMode === QueueRepeatMode.TRACK
? interaction.translate("music/nowplaying:TRACK")
: interaction.translate("common:DISABLED")
}\``,
},
])
.setColor(client.config.embed.color)
.setFooter(client.config.embed.footer)
.setTimestamp();
const row1 = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("nowp_prev_track").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
new ButtonBuilder().setCustomId("nowp_loop").setStyle(ButtonStyle.Secondary).setEmoji("🔁"),
new ButtonBuilder().setCustomId("nowp_add_track").setStyle(ButtonStyle.Success).setEmoji("▶️"),
new ButtonBuilder().setCustomId("nowp_next_track").setStyle(ButtonStyle.Primary).setEmoji("➡️"),
);
const row2 = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("nowp_queue").setStyle(ButtonStyle.Secondary).setEmoji(""),
new ButtonBuilder().setCustomId("nowp_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
const embed = await updateEmbed(interaction, queue);
interaction.editReply({
embeds: [embed],
components: [row1, row2],
});
}
}
/**
*
* @param {import("discord.js").ChatInputCommandInteraction} interaction
* @param {import("discord-player").GuildQueue} queue
* @returns
*/
async function updateEmbed(interaction, queue) {
const progressBar = queue.node.createProgressBar(),
track = queue.currentTrack,
data = await interaction.client.guildsData.findOne({ id: interaction.guildId }),
translated = {
"AUTOPLAY": interaction.translate("music/nowplaying:AUTOPLAY"),
"QUEUE": interaction.translate("music/nowplaying:QUEUE"),
"TRACK": interaction.translate("music/nowplaying:TRACK"),
"OFF": interaction.translate("common:DISABLED"),
"0": interaction.translate("common:DISABLED"),
};
const embed = new EmbedBuilder()
.setAuthor({
name: interaction.translate("music/nowplaying:CURRENTLY_PLAYING"),
})
.setThumbnail(track.thumbnail)
.addFields([
{
name: interaction.translate("music/nowplaying:T_TITLE"),
value: `[${track.title}](${track.url})`,
inline: true,
},
{
name: interaction.translate("music/nowplaying:T_AUTHOR"),
value: track.author || interaction.translate("common:UNKNOWN"),
inline: true,
},
{ name: "\u200B", value: "\u200B", inline: true },
{
name: interaction.translate("common:VIEWS"),
value: track.raw.live ? "Live" : new Intl.NumberFormat(interaction.client.languages.find(language => language.name === data.language).moment, { notation: "standard" }).format(track.raw.views),
inline: true,
},
{
name: interaction.translate("music/queue:ADDED"),
value: track.requestedBy.toString(),
inline: true,
},
{
name: interaction.translate("music/nowplaying:T_DURATION"),
value: progressBar,
},
{
name: "\u200b",
value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\``,
},
])
.setColor(interaction.client.config.embed.color)
.setFooter(interaction.client.config.embed.footer)
.setTimestamp();
return embed;
}
module.exports = Nowplaying;

80
commands/Music/play.c.js Normal file
View file

@ -0,0 +1,80 @@
const { ContextMenuCommandBuilder, ApplicationCommandType, PermissionsBitField } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class PlayContext extends BaseCommand {
/**
*
* @param {import("../../base/JaBa")} client
*/
constructor() {
super({
command: new ContextMenuCommandBuilder()
.setName("Add to Queue")
.setType(ApplicationCommandType.Message)
.setDMPermission(false),
aliases: [],
dirname: __dirname,
ownerOnly: false,
});
}
/**
*
* @param {import("../../base/JaBa")} client
*/
async onLoad() {
//...
}
/**
*
* @param {import("../../base/JaBa")} client
* @param {import("discord.js").MessageContextMenuCommandInteraction} interaction
* @param {Object} data
*/
async execute(client, interaction) {
await interaction.deferReply();
const query = interaction.targetMessage.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)[0],
voice = interaction.member.voice.channel;
if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL", null, { edit: true });
const perms = voice.permissionsFor(client.user);
if (!perms.has(PermissionsBitField.Flags.Connect) || !perms.has(PermissionsBitField.Flags.Speak)) return interaction.error("music/play:VOICE_CHANNEL_CONNECT", null, { edit: true });
const searchResult = await client.player.search(query, {
requestedBy: interaction.user,
});
if (!searchResult.hasTracks()) return interaction.error("music/play:NO_RESULT", { query }, { edit: true });
else {
const { queue } = await client.player.play(interaction.member.voice.channel, searchResult, {
nodeOptions: {
metadata: {
client,
channel: interaction.channel,
requestedBy: interaction.user,
},
},
selfDeaf: true,
leaveOnEnd: false,
leaveOnStop: true,
skipOnNoStream: true,
maxSize: 200,
maxHistorySize: 50,
});
interaction.editReply({
content: interaction.translate("music/play:ADDED_QUEUE", {
songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title,
}, "success"),
});
if (client.player.nodes.get(interaction.guildId).currentTrack.url === query && query.match(/&t=[[0-9]+/g) !== null) {
const time = query.match(/&t=[[0-9]+/g)[0].split("=")[1];
queue.node.seek(time * 1000);
}
}
}
}
module.exports = PlayContext;

View file

@ -80,16 +80,16 @@ class Play extends BaseCommand {
interaction.editReply({
content: interaction.translate("music/play:ADDED_QUEUE", {
songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title,
}),
}, "success"),
});
if (query.match(/&t=[[0-9]+/g) != null) {
// TODO: Seeks currently playing D:
if (query.match(/&t=[[0-9]+/g) !== null) {
const time = query.match(/&t=[[0-9]+/g)[0].split("=")[1];
queue.node.seek(time * 1000);
}
}
}
/**
@ -100,8 +100,9 @@ class Play extends BaseCommand {
*/
async autocompleteRun(client, interaction) {
const query = interaction.options.getString("query");
if (query === "") return;
if (query.includes("http")) return interaction.respond([
if (query.startsWith("http")) return interaction.respond([
{
name: "Current link",
value: query,

View file

@ -1,5 +1,4 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"),
{ QueueRepeatMode } = require("discord-player");
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Queue extends BaseCommand {
@ -26,8 +25,94 @@ class Queue extends BaseCommand {
*
* @param {import("../../base/JaBa")} client
*/
async onLoad() {
//...
async onLoad(client) {
client.on("interactionCreate", async interaction => {
if (!interaction.isButton()) return;
if (interaction.customId.startsWith("queue_")) {
const queue = client.player.nodes.get(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING");
const { embeds, size } = generateQueueEmbeds(client, interaction, queue);
let currentPage = Number(interaction.message.content.match(/\d+/g)[0]) - 1 ?? 0;
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("queue_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
new ButtonBuilder().setCustomId("queue_next_page").setStyle(ButtonStyle.Primary).setEmoji("➡️"),
new ButtonBuilder().setCustomId("queue_jump_page").setStyle(ButtonStyle.Secondary).setEmoji("↗️"),
new ButtonBuilder().setCustomId("queue_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
if (interaction.customId === "queue_prev_page") {
await interaction.deferUpdate();
if (currentPage !== 0) {
--currentPage;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${size}**`,
embeds: [embeds[currentPage]],
components: [row],
});
}
} else if (interaction.customId === "queue_next_page") {
await interaction.deferUpdate();
if (currentPage < size - 1) {
currentPage++;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${size}**`,
embeds: [embeds[currentPage]],
components: [row],
});
}
} else if (interaction.customId === "queue_jump_page") {
await interaction.deferUpdate();
const selectMenu = new StringSelectMenuBuilder()
.setCustomId("queue_select")
.setPlaceholder(interaction.translate("common:NOTHING_SELECTED"));
for (let i = 0; i < size; i++) {
selectMenu.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel(`${i + 1}`)
.setValue(`${i}`),
);
}
const selectRow = new ActionRowBuilder().addComponents(selectMenu),
msg = await interaction.followUp({
components: [selectRow],
ephemeral: true,
});
const filter = i => i.user.id === interaction.user.id,
collected = await msg.awaitMessageComponent({ filter, time: 10 * 1000 }),
page = Number(collected.values[0]);
await collected.deferUpdate();
return interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${page + 1}**/**${size}**`,
embeds: [embeds[page]],
components: [row],
});
} else if (interaction.customId === "queue_stop") {
await interaction.deferUpdate();
row.components.forEach(component => {
component.setDisabled(true);
});
return interaction.editReply({
components: [row],
});
}
}
});
}
/**
*
@ -39,98 +124,24 @@ class Queue extends BaseCommand {
const queue = client.player.nodes.get(interaction.guildId);
if (!queue) return interaction.error("music/play:NOT_PLAYING");
let currentPage = 0;
let embeds = generateQueueEmbeds(client, interaction, queue);
const { embeds, size } = generateQueueEmbeds(client, interaction, queue),
row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("queue_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
new ButtonBuilder().setCustomId("queue_next_page").setStyle(ButtonStyle.Primary).setEmoji("➡️"),
new ButtonBuilder().setCustomId("queue_jump_page").setStyle(ButtonStyle.Secondary).setEmoji("↗️"),
new ButtonBuilder().setCustomId("queue_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("queue_prev_page").setLabel(interaction.translate("music/queue:PREV_PAGE")).setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
new ButtonBuilder().setCustomId("queue_next_page").setLabel(interaction.translate("music/queue:NEXT_PAGE")).setStyle(ButtonStyle.Primary).setEmoji("➡️"),
new ButtonBuilder().setCustomId("queue_jump_page").setLabel(interaction.translate("music/queue:JUMP_PAGE")).setStyle(ButtonStyle.Secondary).setEmoji("↗️"),
new ButtonBuilder().setCustomId("queue_stop").setLabel(interaction.translate("common:CANCEL")).setStyle(ButtonStyle.Danger).setEmoji("⏹️"),
);
await interaction.reply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
if (interaction.customId) return await interaction.followUp({
content: `${interaction.translate("common:PAGE")}: **1**/**${size}**`,
embeds: [embeds[0]],
components: [row],
});
const filter = i => i.user.id === interaction.user.id;
const collector = interaction.channel.createMessageComponentCollector({ filter, idle: 20 * 1000 });
collector.on("collect", async i => {
if (i.isButton()) {
if (i.customId === "queue_prev_page") {
i.deferUpdate();
if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue);
if (currentPage !== 0) {
--currentPage;
interaction.editReply({
content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`,
embeds: [embeds[currentPage]],
components: [row],
});
}
} else if (i.customId === "queue_next_page") {
i.deferUpdate();
if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue);
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 === "queue_jump_page") {
i.deferUpdate();
if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue);
const msg = await interaction.followUp({
content: interaction.translate("misc:JUMP_TO_PAGE", {
length: embeds.length,
}),
fetchReply: true,
});
const filter = res => {
return res.author.id === interaction.user.id && !isNaN(res.content);
};
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 === "queue_stop") {
i.deferUpdate();
collector.stop();
}
}
});
collector.on("end", () => {
row.components.forEach(component => {
component.setDisabled(true);
});
return interaction.editReply({
components: [row],
});
await interaction.reply({
content: `${interaction.translate("common:PAGE")}: **1**/**${size}**`,
embeds: [embeds[0]],
components: [row],
});
}
}
@ -144,7 +155,14 @@ class Queue extends BaseCommand {
*/
function generateQueueEmbeds(client, interaction, queue) {
const embeds = [],
currentTrack = queue.currentTrack;
currentTrack = queue.currentTrack,
translated = {
"AUTOPLAY": interaction.translate("music/nowplaying:AUTOPLAY"),
"QUEUE": interaction.translate("music/nowplaying:QUEUE"),
"TRACK": interaction.translate("music/nowplaying:TRACK"),
"OFF": interaction.translate("common:DISABLED"),
"0": interaction.translate("common:DISABLED"),
};
let k = 10;
@ -154,22 +172,14 @@ function generateQueueEmbeds(client, interaction, queue) {
.setThumbnail(currentTrack.thumbnail)
.setColor(interaction.client.config.embed.color)
.setDescription(
`${interaction.translate("music/nowplaying:REPEAT")}: \`${
queue.repeatMode === QueueRepeatMode.AUTOPLAY
? interaction.translate("music/nowplaying:AUTOPLAY")
: queue.repeatMode === QueueRepeatMode.QUEUE
? interaction.translate("music/nowplaying:QUEUE")
: queue.repeatMode === QueueRepeatMode.TRACK
? interaction.translate("music/nowplaying:TRACK")
: interaction.translate("common:DISABLED")
}\`\n${currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`}\n> ${interaction.translate("music/queue:ADDED")} ${
currentTrack.requestedBy
}\n\n**${interaction.translate("music/queue:NEXT")}**\n${interaction.translate("music/queue:NO_QUEUE")}`,
`${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\`\n${
currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`
}\n> ${interaction.translate("music/queue:ADDED")} ${currentTrack.requestedBy}\n\n**${interaction.translate("music/queue:NEXT")}**\n${interaction.translate("music/queue:NO_QUEUE")}`,
)
.setTimestamp();
embeds.push(embed);
return embeds;
return { embeds: embeds, size: embeds.length };
}
for (let i = 0; i < queue.getSize(); i += 10) {
@ -184,15 +194,9 @@ function generateQueueEmbeds(client, interaction, queue) {
.setThumbnail(currentTrack.thumbnail)
.setColor(interaction.client.config.embed.color)
.setDescription(
`${interaction.translate("music/nowplaying:REPEAT")}: \`${
queue.repeatMode === QueueRepeatMode.AUTOPLAY
? interaction.translate("music/nowplaying:AUTOPLAY")
: queue.repeatMode === QueueRepeatMode.QUEUE
? interaction.translate("music/nowplaying:QUEUE")
: queue.repeatMode === QueueRepeatMode.TRACK
? interaction.translate("music/nowplaying:TRACK")
: interaction.translate("common:DISABLED")
}\`\n${currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`}\n> ${interaction.translate("music/queue:ADDED")} ${
`${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\`\n${
currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`
}\n * ${interaction.translate("music/queue:ADDED")} ${
currentTrack.requestedBy
}\n\n**${interaction.translate("music/queue:NEXT")}**\n${info}`,
)
@ -201,7 +205,7 @@ function generateQueueEmbeds(client, interaction, queue) {
embeds.push(embed);
}
return embeds;
return { embeds: embeds, size: embeds.length };
}
module.exports = Queue;

View file

@ -70,7 +70,7 @@ class NSFW extends BaseCommand {
),
);
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("nsfw_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(tags));
const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("nsfw_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(tags));
await interaction.editReply({
content: interaction.translate("common:AVAILABLE_OPTIONS"),

View file

@ -61,7 +61,7 @@ class CloseTicket extends BaseCommand {
const button = new ButtonBuilder().setCustomId("cancel_closing").setLabel(interaction.translate("common:CANCEL")).setStyle(ButtonStyle.Danger);
const row = new ActionRowBuilder().addComponents(button);
await interaction.reply({
await interaction.editReply({
embeds: [embed],
components: [row],
});

@ -1 +1 @@
Subproject commit 58c2082074c0955cfafd9ebddb9ad694621df0a1
Subproject commit 2826563af194effcb0469278ab3c38aa27bc2e82

View file

@ -17,11 +17,11 @@ const locales = {
module.exports.load = async client => {
const commands = client.commands.map(v => {
return {
_category: v.category,
commandName: v.command.name,
commandDescription: client.translate(`${v.category.toLowerCase()}/${v.command.name}:DESCRIPTION`),
commandUsage: client.translate(`${v.category.toLowerCase()}/${v.command.name}:USAGE`),
commandAlias: "",
_category: v.category,
};
});
let categories = [];
@ -48,11 +48,6 @@ module.exports.load = async client => {
id: client.config.userId,
secret: client.config.dashboard.secret,
},
// SSL: {
// enabled: false,
// key: fs.readFileSync(`${__dirname}/../jababot-cloudflare.key`, "utf-8"),
// cert: fs.readFileSync(`${__dirname}/../jababot-cloudflare.crt`, "utf-8"),
// },
cookiesSecret: client.config.dashboard.secret,
domain: client.config.dashboard.domain,
redirectUri: `${client.config.dashboard.domain}/discord/callback`,

View file

@ -1,27 +1,40 @@
# Обновления JaBa
## v4.4.0
* Добавлено
* Контекстная команда *Add to Queue* - Позволяет добавить в очередь трек/плейлист из сообщения.\
ПКМ на сообщение -> Приложения -> Add to Queue.\
Работает даже если в сообщении есть не только ссылка `Попробуй это: <ссылка>` или ссылка встроенна в текст `[текст](<ссылка>)`.
* Контекстная комана *Avatar* - Позволет получить аватар пользователя\
Работает так же как и *Add to Queue*, только нужно нажать на пользователя.
* Изменено
* Переписаны команды *queue*, *play*, *nowplaying*.\
В *nowplaying* добавлен дополнительный функционал в виде кнопок.
## v4.3.6
Скоро перепишу музыкальные команды, будет интересно, честно =)
* Изменено
* Переписал команды которые используют кнопки и списки, многие из них теперь будут отвечать даже после перезагрузки бота.
* Переписаны команды которые используют кнопки и списки, многие из них теперь будут отвечать даже после перезагрузки бота.
## v4.3.5
* Добавлено
* Логи удаления сообщений!\
Настройку можно найти в *config set* и в панели управления.
Настройку можно найти в *config set* и в панели управления.
* Начало проигрывания видео с указанного в ссылке времени.
### v4.3.3
* Добавлено
* Система Тикетов!\
Все необходимые команды есть в категории *Tickets*.\
Для создавания тикетов нужно обязательно выбрать категорию где они будут сохраняться, сделать это можно через *config set*!\
После этого используйте команду *createticketembed* в канале, где хотите чтобы появилось сообщение с кнопкой создания тикета.\
С помощью команд *adduser* и *removeuser* можно добавлять и убирать людей из тикета соответственно.\
Все необходимые команды есть в категории *Tickets*.\
Для создавания тикетов нужно обязательно выбрать категорию где они будут сохраняться, сделать это можно через *config set*!\
После этого используйте команду *createticketembed* в канале, где хотите чтобы появилось сообщение с кнопкой создания тикета.\
С помощью команд *adduser* и *removeuser* можно добавлять и убирать людей из тикета соответственно.\
*closeticket* позволяет принудительно закрыть тикет.
* Изменено
@ -31,7 +44,7 @@
* Добавлено
* Мониторинг изменения сообщений!\
Скоро добавлю другие эвенты, по типу входа, выхода, обновление участника.
Скоро добавлю другие эвенты, по типу входа, выхода, обновление участника.
* Исправления
* Серьёзная ошибка, из-за которой данные не сохранялись в базу данных 🤯.
@ -40,7 +53,7 @@
* Добавлено
* Полностью переделанная панель управления!\
Она ещё не закончена, так что ждите продолжения.
Она ещё не закончена, так что ждите продолжения.
### v4.2.6
@ -49,13 +62,13 @@
* Изменено
* Отключено уведомление об ачивках, включу когда переделаю.\
Хочу отправлять их в ЛС.
Хочу отправлять их в ЛС.
### v4.2.5
* Изменено
* Изменения в локализации, основным языком теперь является английский.\
Подсказки по командам зависят от языка вашего клиента, ответ зависит от языка сервера.
Подсказки по командам зависят от языка вашего клиента, ответ зависит от языка сервера.
* Отключена команда *memes*.
* Отключена команда *staff*.
@ -147,9 +160,9 @@
### v4.1.15
* Изменения
* Переписана команда *config*.
* Теперь с её помощью можно просматривать и изменять настройки сервера.
* Использование смотрите в *help*.
* Переписана команда *config*.\
Теперь с её помощью можно просматривать и изменять настройки сервера.\
Использование смотрите в *help*.
* Удалено
* Команды *setbirthdays*, *setmodlogs*, *setnews*, *setreports*, *setsuggests*.
@ -157,9 +170,9 @@
### v4.1.14
* Добавлено
* Команда *selectroles* - Возможность выбора необязательных ролей пользователями.
* Сначала необходимо создать сообщение через */selectroles message text:<Ваш текст>*.
* После того как сообщение будет создано бот даст подсказку как добавлять роли к списку, следуйте инструкции.
* Команда *selectroles* - Возможность выбора необязательных ролей пользователями.\
Сначала необходимо создать сообщение через */selectroles message text:<Ваш текст>*.\
После того как сообщение будет создано бот даст подсказку как добавлять роли к списку, следуйте инструкции.
* Изменения
* Украинский перевод полностью обновлён.
@ -222,7 +235,8 @@
### v4.1.6
* Изменения
* Изменён способ указания типа повтора в *loop*. Теперь вы указываете тип аргументом (подсказки имеются), а не из выпадающего списка в отдельном сообщении. Это одновременно удобно, быстро и меньше кода =)
* Изменён способ указания типа повтора в *loop*.\
Теперь вы указываете тип аргументом (подсказки имеются), а не из выпадающего списка в отдельном сообщении. Это одновременно удобно, быстро и кода меньше =)
* Исправления
* Фиксы в *tictactoe*.
@ -293,7 +307,8 @@
* Перенесена категория *Administration*.
* Исправления
* Изменён способ получения музыки. На данный момент не работает Spotify, в будущих обновлениях постараюсь вернуть, но это не точно.
* Изменён способ получения музыки.\
На данный момент не работает Spotify, в будущих обновлениях постараюсь вернуть, но это не точно.
* Панель управления снова работает.
* Множество мелких правок.

View file

@ -4,15 +4,15 @@ User.prototype.getUsername = function () {
return this.discriminator === "0" ? this.username : this.tag;
};
BaseInteraction.prototype.translate = function (key, args) {
const language = this.client.translations.get(this.guild ? this.guild.data.language : "en-US");
if (!language) throw "Interaction: Invalid language set in data.";
BaseInteraction.prototype.translate = function (key, args, emoji) {
const lang = this.client.translations.get(this.guild.data.language ?? "en-US");
const string = emoji ? `${this.client.customEmojis[emoji]} | ${lang(key, args)}` : lang(key, args);
return language(key, args);
return string;
};
BaseInteraction.prototype.replyT = function (key, args, options = {}) {
const translated = this.translate(key, args, this.guild ? this.guild.data.language : "en-US");
const translated = this.translate(key, args, this.guild.data.language ?? "en-US");
const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated;
if (options.edit) return this.editReply({ content: string, ephemeral: options.ephemeral || false });
@ -31,15 +31,15 @@ BaseInteraction.prototype.error = function (key, args, options = {}) {
return this.replyT(key, args, options);
};
Message.prototype.translate = function (key, args) {
const language = this.client.translations.get(this.guild ? this.guild.data.language : "en-US");
if (!language) throw "Message: Invalid language set in data.";
Message.prototype.translate = function (key, args, emoji) {
const lang = this.client.translations.get(this.guild.data.language ?? "en-US");
const string = emoji ? `${this.client.customEmojis[emoji]} | ${lang(key, args)}` : lang(key, args);
return language(key, args);
return string;
};
Message.prototype.replyT = function (key, args, options = {}) {
const translated = this.translate(key, args, this.guild ? this.guild.data.language : "en-US");
const translated = this.translate(key, args, this.guild.data.language ?? "en-US");
const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated;
if (options.edit) return this.edit({ content: string, allowedMentions: { repliedUser: options.mention ? true : false } });

View file

@ -32,6 +32,7 @@
"IP": "IP Address",
"JOINED": "Joined",
"LANGUAGE": "Language",
"LINK": "Link",
"LEVEL": "Level",
"MONEY": "Money",
"MEMBER": "Member",

View file

@ -3,6 +3,7 @@
"USAGE": "",
"EXAMPLES": "nowplaying",
"CURRENTLY_PLAYING": "Currently playing",
"LINK": "Send a link in chat",
"T_TITLE": "Title",
"T_AUTHOR": "Author",
"T_DURATION": "Duration",

View file

@ -32,6 +32,7 @@
"IP": "IP адрес",
"JOINED": "Присоеденился",
"LANGUAGE": "Язык",
"LINK": "Ссылка",
"LEVEL": "Уровень",
"MONEY": "Деньги",
"MEMBER": "Участник",

View file

@ -3,5 +3,5 @@
"USAGE": "",
"EXAMPLES": "back",
"NO_PREV_SONG": "Предыдущий трек отсутствует",
"SUCCESS": "Играет предыдущий трек"
"SUCCESS": "Включён предыдущий трек"
}

View file

@ -3,6 +3,7 @@
"USAGE": "",
"EXAMPLES": "nowplaying",
"CURRENTLY_PLAYING": "Сейчас играет",
"LINK": "Введите ссылку в чат",
"T_TITLE": "Название",
"T_AUTHOR": "Автор",
"T_DURATION": "Длительность",

View file

@ -31,6 +31,7 @@
"IP": "IP-адреса",
"JOINED": "Приєднався",
"LANGUAGE": "Мова",
"LINK": "Силка",
"LEVEL": "Рівень",
"MONEY": "Гроші",
"MEMBER": "Учасник",

View file

@ -3,5 +3,5 @@
"USAGE": "",
"EXAMPLES": "back",
"NO_PREV_SONG": "Попередній трек відсутній",
"SUCCESS": "Грає попередній трек"
"SUCCESS": "Включений попередній трек"
}

View file

@ -3,6 +3,7 @@
"USAGE": "",
"EXAMPLES": "nowplaying",
"CURRENTLY_PLAYING": "Зараз грає",
"LINK": "Введіть силку на чат",
"T_TITLE": "Назва",
"T_AUTHOR": "Автор",
"T_DURATION": "Тривалість",

View file

@ -1,6 +1,6 @@
{
"name": "jaba",
"version": "4.3.6",
"version": "4.4.0",
"description": "My Discord Bot",
"main": "index.js",
"scripts": {
@ -8,7 +8,7 @@
"start": "node .",
"lint": "eslint . --ext .js"
},
"author": "Discord: jonny_bro",
"author": "Discord: @jonny_bro",
"dependencies": {
"@discord-player/extractor": "^4.4.5",
"@discordjs/opus": "^0.9.0",
@ -35,13 +35,13 @@
"eslint": "^8.52.0"
},
"eslintConfig": {
"extends": "eslint:recommended",
"env": {
"commonjs": true,
"es6": true,
"es2020": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"