Apollo Initial Files
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { bot } from "../index";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("help").setDescription(i18n.__("help.description")),
|
||||
async execute(interaction: CommandInteraction) {
|
||||
let commands = bot.slashCommandsMap;
|
||||
|
||||
let helpEmbed = new EmbedBuilder()
|
||||
.setTitle(i18n.__mf("help.embedTitle", { botname: interaction.client.user!.username }))
|
||||
.setDescription(i18n.__("help.embedDescription"))
|
||||
.setColor("#F8AA2A");
|
||||
|
||||
commands.forEach((cmd) => {
|
||||
helpEmbed.addFields({
|
||||
name: `**${cmd.data.name}**`,
|
||||
value: `${cmd.data.description}`,
|
||||
inline: true
|
||||
});
|
||||
});
|
||||
|
||||
helpEmbed.setTimestamp();
|
||||
|
||||
return interaction.reply({ embeds: [helpEmbed] }).catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ChatInputCommandInteraction,
|
||||
EmbedBuilder,
|
||||
SlashCommandBuilder
|
||||
} from "discord.js";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("invite").setDescription(i18n.__("invite.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const inviteEmbed = new EmbedBuilder().setTitle(i18n.__mf("Invite me to your server!"));
|
||||
|
||||
// return interaction with embed and button to invite the bot
|
||||
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel(i18n.__mf("Invite"))
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL(
|
||||
`https://discord.com/api/oauth2/authorize?client_id=${
|
||||
interaction.client.user!.id
|
||||
}&permissions=8&scope=bot%20applications.commands`
|
||||
)
|
||||
);
|
||||
|
||||
return interaction.reply({ embeds: [inviteEmbed], components: [actionRow] }).catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("loop").setDescription(i18n.__("loop.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("loop.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!guildMemer || !canModifyQueue(guildMemer)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
queue.loop = !queue.loop;
|
||||
|
||||
const content = i18n.__mf("loop.result", { loop: queue.loop ? i18n.__("common.on") : i18n.__("common.off") });
|
||||
|
||||
safeReply(interaction, content);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
|
||||
import { i18n } from "../utils/i18n";
|
||||
// @ts-ignore
|
||||
import lyricsFinder from "lyrics-finder";
|
||||
import { bot } from "../index";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("lyrics").setDescription(i18n.__("lyrics.description")),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue || !queue.songs.length) return interaction.reply(i18n.__("lyrics.errorNotQueue")).catch(console.error);
|
||||
|
||||
await interaction.reply("⏳ Loading...").catch(console.error);
|
||||
|
||||
let lyrics = null;
|
||||
const title = queue.songs[0].title;
|
||||
|
||||
try {
|
||||
lyrics = await lyricsFinder(queue.songs[0].title, "");
|
||||
if (!lyrics) lyrics = i18n.__mf("lyrics.lyricsNotFound", { title: title });
|
||||
} catch (error) {
|
||||
lyrics = i18n.__mf("lyrics.lyricsNotFound", { title: title });
|
||||
}
|
||||
|
||||
let lyricsEmbed = new EmbedBuilder()
|
||||
.setTitle(i18n.__mf("lyrics.embedTitle", { title: title }))
|
||||
.setDescription(lyrics.length >= 4096 ? `${lyrics.substr(0, 4093)}...` : lyrics)
|
||||
.setColor("#F8AA2A")
|
||||
.setTimestamp();
|
||||
|
||||
return interaction.editReply({ content: "", embeds: [lyricsEmbed] }).catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import move from "array-move";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("move")
|
||||
.setDescription(i18n.__("move.description"))
|
||||
.addIntegerOption((option) =>
|
||||
option.setName("movefrom").setDescription(i18n.__("move.args.movefrom")).setRequired(true)
|
||||
)
|
||||
.addIntegerOption((option) =>
|
||||
option.setName("moveto").setDescription(i18n.__("move.args.moveto")).setRequired(true)
|
||||
),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const movefromArg = interaction.options.getInteger("movefrom");
|
||||
const movetoArg = interaction.options.getInteger("moveto");
|
||||
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue) return interaction.reply(i18n.__("move.errorNotQueue")).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return;
|
||||
|
||||
if (!movefromArg || !movetoArg)
|
||||
return interaction.reply({ content: i18n.__mf("move.usagesReply", { prefix: bot.prefix }), ephemeral: true });
|
||||
|
||||
if (isNaN(movefromArg) || movefromArg <= 1)
|
||||
return interaction.reply({ content: i18n.__mf("move.usagesReply", { prefix: bot.prefix }), ephemeral: true });
|
||||
|
||||
let song = queue.songs[movefromArg - 1];
|
||||
|
||||
queue.songs = move(queue.songs, movefromArg - 1, movetoArg == 1 ? 1 : movetoArg - 1);
|
||||
|
||||
interaction.reply({
|
||||
content: i18n.__mf("move.result", {
|
||||
author: interaction.user.id,
|
||||
title: song.title,
|
||||
index: movetoArg == 1 ? 1 : movetoArg
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
|
||||
import { splitBar } from "string-progressbar";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("nowplaying").setDescription(i18n.__("nowplaying.description")),
|
||||
cooldown: 10,
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue || !queue.songs.length)
|
||||
return interaction.reply({ content: i18n.__("nowplaying.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
const song = queue.songs[0];
|
||||
const seek = queue.resource.playbackDuration / 1000;
|
||||
const left = song.duration - seek;
|
||||
|
||||
let nowPlaying = new EmbedBuilder()
|
||||
.setTitle(i18n.__("nowplaying.embedTitle"))
|
||||
.setDescription(`${song.title}\n${song.url}`)
|
||||
.setColor("#F8AA2A");
|
||||
|
||||
if (song.duration > 0) {
|
||||
nowPlaying.addFields({
|
||||
name: "\u200b",
|
||||
value:
|
||||
new Date(seek * 1000).toISOString().substr(11, 8) +
|
||||
"[" +
|
||||
splitBar(song.duration == 0 ? seek : song.duration, seek, 20)[0] +
|
||||
"]" +
|
||||
(song.duration == 0 ? " ◉ LIVE" : new Date(song.duration * 1000).toISOString().substr(11, 8)),
|
||||
inline: false
|
||||
});
|
||||
|
||||
nowPlaying.setFooter({
|
||||
text: i18n.__mf("nowplaying.timeRemaining", {
|
||||
time: new Date(left * 1000).toISOString().substr(11, 8)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return interaction.reply({ embeds: [nowPlaying] });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("pause").setDescription(i18n.__("pause.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue) return interaction.reply({ content: i18n.__("pause.errorNotQueue") }).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
if (queue.player.pause()) {
|
||||
const content = i18n.__mf("pause.result", { author: interaction.user.id });
|
||||
|
||||
safeReply(interaction, content);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("ping").setDescription(i18n.__("ping.description")),
|
||||
cooldown: 10,
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
interaction
|
||||
.reply({ content: i18n.__mf("ping.result", { ping: Math.round(interaction.client.ws.ping) }), ephemeral: true })
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
import { DiscordGatewayAdapterCreator, joinVoiceChannel } from "@discordjs/voice";
|
||||
import { ChatInputCommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { MusicQueue } from "../structs/MusicQueue";
|
||||
import { Song } from "../structs/Song";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { playlistPattern } from "../utils/patterns";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("play")
|
||||
.setDescription(i18n.__("play.description"))
|
||||
.addStringOption((option) => option.setName("song").setDescription("The song you want to play").setRequired(true)),
|
||||
cooldown: 3,
|
||||
permissions: [PermissionsBitField.Flags.Connect, PermissionsBitField.Flags.Speak],
|
||||
async execute(interaction: ChatInputCommandInteraction, input: string) {
|
||||
let argSongName = interaction.options.getString("song");
|
||||
if (!argSongName) argSongName = input;
|
||||
|
||||
const guildMember = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const { channel } = guildMember!.voice;
|
||||
|
||||
if (!channel)
|
||||
return interaction.reply({ content: i18n.__("play.errorNotChannel"), ephemeral: true }).catch(console.error);
|
||||
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (queue && channel.id !== queue.connection.joinConfig.channelId)
|
||||
return interaction
|
||||
.reply({
|
||||
content: i18n.__mf("play.errorNotInSameChannel", { user: bot.client.user!.username }),
|
||||
ephemeral: true
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
if (!argSongName)
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("play.usageReply", { prefix: bot.prefix }), ephemeral: true })
|
||||
.catch(console.error);
|
||||
|
||||
const url = argSongName;
|
||||
|
||||
if (interaction.replied) await interaction.editReply("⏳ Loading...").catch(console.error);
|
||||
else await interaction.reply("⏳ Loading...");
|
||||
|
||||
// Start the playlist if playlist url was provided
|
||||
if (playlistPattern.test(url)) {
|
||||
await interaction.editReply("🔗 Link is playlist").catch(console.error);
|
||||
|
||||
return bot.slashCommandsMap.get("playlist")!.execute(interaction, "song");
|
||||
}
|
||||
|
||||
let song;
|
||||
|
||||
try {
|
||||
song = await Song.from(url, url);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
|
||||
if (error.name == "NoResults")
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("play.errorNoResults", { url: `<${url}>` }), ephemeral: true })
|
||||
.catch(console.error);
|
||||
|
||||
if (error.name == "InvalidURL")
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("play.errorInvalidURL", { url: `<${url}>` }), ephemeral: true })
|
||||
.catch(console.error);
|
||||
|
||||
if (interaction.replied)
|
||||
return await interaction.editReply({ content: i18n.__("common.errorCommand") }).catch(console.error);
|
||||
else return interaction.reply({ content: i18n.__("common.errorCommand"), ephemeral: true }).catch(console.error);
|
||||
}
|
||||
|
||||
if (queue) {
|
||||
queue.enqueue(song);
|
||||
|
||||
return (interaction.channel as TextChannel)
|
||||
.send({ content: i18n.__mf("play.queueAdded", { title: song.title, author: interaction.user.id }) })
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
const newQueue = new MusicQueue({
|
||||
interaction,
|
||||
textChannel: interaction.channel! as TextChannel,
|
||||
connection: joinVoiceChannel({
|
||||
channelId: channel.id,
|
||||
guildId: channel.guild.id,
|
||||
adapterCreator: channel.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
|
||||
})
|
||||
});
|
||||
|
||||
bot.queues.set(interaction.guild!.id, newQueue);
|
||||
|
||||
newQueue.enqueue(song);
|
||||
interaction.deleteReply().catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import { DiscordGatewayAdapterCreator, joinVoiceChannel } from "@discordjs/voice";
|
||||
import {
|
||||
ChatInputCommandInteraction,
|
||||
EmbedBuilder,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
TextChannel
|
||||
} from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { MusicQueue } from "../structs/MusicQueue";
|
||||
import { Playlist } from "../structs/Playlist";
|
||||
import { Song } from "../structs/Song";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("playlist")
|
||||
.setDescription(i18n.__("playlist.description"))
|
||||
.addStringOption((option) => option.setName("playlist").setDescription("Playlist name or link").setRequired(true)),
|
||||
cooldown: 5,
|
||||
permissions: [PermissionsBitField.Flags.Connect, PermissionsBitField.Flags.Speak],
|
||||
async execute(interaction: ChatInputCommandInteraction, queryOptionName = "playlist") {
|
||||
let argSongName = interaction.options.getString(queryOptionName);
|
||||
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const { channel } = guildMemer!.voice;
|
||||
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!channel)
|
||||
return interaction.reply({ content: i18n.__("playlist.errorNotChannel"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (queue && channel.id !== queue.connection.joinConfig.channelId)
|
||||
if (interaction.replied)
|
||||
return interaction
|
||||
.editReply({ content: i18n.__mf("play.errorNotInSameChannel", { user: interaction.client.user!.username }) })
|
||||
.catch(console.error);
|
||||
else
|
||||
return interaction
|
||||
.reply({
|
||||
content: i18n.__mf("play.errorNotInSameChannel", { user: interaction.client.user!.username }),
|
||||
ephemeral: true
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
let playlist;
|
||||
|
||||
try {
|
||||
playlist = await Playlist.from(argSongName!.split(" ")[0], argSongName!);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (interaction.replied)
|
||||
return interaction.editReply({ content: i18n.__("playlist.errorNotFoundPlaylist") }).catch(console.error);
|
||||
else
|
||||
return interaction
|
||||
.reply({ content: i18n.__("playlist.errorNotFoundPlaylist"), ephemeral: true })
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
if (queue) {
|
||||
queue.songs.push(...playlist.videos);
|
||||
} else {
|
||||
const newQueue = new MusicQueue({
|
||||
interaction,
|
||||
textChannel: interaction.channel! as TextChannel,
|
||||
connection: joinVoiceChannel({
|
||||
channelId: channel.id,
|
||||
guildId: channel.guild.id,
|
||||
adapterCreator: channel.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
|
||||
})
|
||||
});
|
||||
|
||||
bot.queues.set(interaction.guild!.id, newQueue);
|
||||
newQueue.enqueue(...playlist.videos);
|
||||
}
|
||||
|
||||
let playlistEmbed = new EmbedBuilder()
|
||||
.setTitle(`${playlist.data.title}`)
|
||||
.setDescription(
|
||||
playlist.videos
|
||||
.map((song: Song, index: number) => `${index + 1}. ${song.title}`)
|
||||
.join("\n")
|
||||
.slice(0, 4095)
|
||||
)
|
||||
.setURL(playlist.data.url!)
|
||||
.setColor("#F8AA2A")
|
||||
.setTimestamp();
|
||||
|
||||
if (interaction.replied)
|
||||
return interaction.editReply({
|
||||
content: i18n.__mf("playlist.startedPlaylist", { author: interaction.user.id }),
|
||||
embeds: [playlistEmbed]
|
||||
});
|
||||
interaction
|
||||
.reply({
|
||||
content: i18n.__mf("playlist.startedPlaylist", { author: interaction.user.id }),
|
||||
embeds: [playlistEmbed]
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ChatInputCommandInteraction,
|
||||
CommandInteraction,
|
||||
EmbedBuilder,
|
||||
Interaction,
|
||||
SlashCommandBuilder
|
||||
} from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { Song } from "../structs/Song";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("queue").setDescription(i18n.__("queue.description")),
|
||||
cooldown: 5,
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
if (!queue || !queue.songs.length) return interaction.reply({ content: i18n.__("queue.errorNotQueue") });
|
||||
|
||||
let currentPage = 0;
|
||||
const embeds = generateQueueEmbed(interaction, queue.songs);
|
||||
|
||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder().setCustomId("previous").setLabel("⬅️").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("stop").setLabel("⏹").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("next").setLabel("➡️").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
await interaction.reply("⏳ Loading queue...");
|
||||
|
||||
if (interaction.replied)
|
||||
await interaction.editReply({
|
||||
content: `**${i18n.__mf("queue.currentPage")} ${currentPage + 1}/${embeds.length}**`,
|
||||
embeds: [embeds[currentPage]],
|
||||
components: [row]
|
||||
});
|
||||
|
||||
const queueEmbed = await interaction.fetchReply();
|
||||
|
||||
const filter = (buttonInteraction: Interaction) =>
|
||||
buttonInteraction.isButton() && buttonInteraction.user.id === interaction.user.id;
|
||||
|
||||
const collector = queueEmbed.createMessageComponentCollector({ filter, time: 60000 });
|
||||
|
||||
const buttonHandlers = {
|
||||
next: async () => {
|
||||
if (currentPage >= embeds.length - 1) return;
|
||||
|
||||
currentPage++;
|
||||
|
||||
await interaction.editReply({
|
||||
content: `**${i18n.__mf("queue.currentPage", {
|
||||
page: currentPage + 1,
|
||||
length: embeds.length
|
||||
})}**`,
|
||||
embeds: [embeds[currentPage]],
|
||||
components: [row]
|
||||
});
|
||||
},
|
||||
previous: async () => {
|
||||
if (currentPage === 0) return;
|
||||
|
||||
currentPage--;
|
||||
await interaction.editReply({
|
||||
content: `**${i18n.__mf("queue.currentPage", {
|
||||
page: currentPage + 1,
|
||||
length: embeds.length
|
||||
})}**`,
|
||||
embeds: [embeds[currentPage]],
|
||||
components: [row]
|
||||
});
|
||||
},
|
||||
stop: async () => {
|
||||
await interaction.editReply({
|
||||
components: []
|
||||
});
|
||||
|
||||
collector.stop();
|
||||
}
|
||||
};
|
||||
|
||||
collector.on("collect", async (buttonInteraction) => {
|
||||
buttonInteraction.deferUpdate();
|
||||
|
||||
const handler = buttonHandlers[buttonInteraction.customId as keyof typeof buttonHandlers];
|
||||
|
||||
if (handler) {
|
||||
await handler();
|
||||
}
|
||||
});
|
||||
|
||||
collector.on("end", () => {
|
||||
queueEmbed
|
||||
.edit({
|
||||
components: []
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function generateQueueEmbed(interaction: CommandInteraction, songs: Song[]) {
|
||||
let embeds = [];
|
||||
let k = 10;
|
||||
|
||||
for (let i = 0; i < songs.length; i += 10) {
|
||||
const current = songs.slice(i, k);
|
||||
let j = i;
|
||||
k += 10;
|
||||
|
||||
const info = current.map((track) => `${++j} - [${track.title}](${track.url})`).join("\n");
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(i18n.__("queue.embedTitle"))
|
||||
.setThumbnail(interaction.guild?.iconURL()!)
|
||||
.setColor("#F8AA2A")
|
||||
.setDescription(i18n.__mf("queue.embedCurrentSong", { title: songs[0].title, url: songs[0].url, info: info }))
|
||||
.setTimestamp();
|
||||
embeds.push(embed);
|
||||
}
|
||||
|
||||
return embeds;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { SlashCommandBuilder, CommandInteraction, ChatInputCommandInteraction } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { Song } from "../structs/Song";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
|
||||
const pattern = /^[0-9]{1,2}(\s*,\s*[0-9]{1,2})*$/;
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("remove")
|
||||
.setDescription(i18n.__("remove.description"))
|
||||
.addStringOption((option) =>
|
||||
option.setName("slot").setDescription(i18n.__("remove.description")).setRequired(true)
|
||||
),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const removeArgs = interaction.options.getString("slot");
|
||||
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("remove.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
if (!removeArgs)
|
||||
return interaction.reply({ content: i18n.__mf("remove.usageReply", { prefix: bot.prefix }), ephemeral: true });
|
||||
|
||||
const songs = removeArgs.split(",").map((arg) => parseInt(arg));
|
||||
|
||||
let removed: Song[] = [];
|
||||
|
||||
if (pattern.test(removeArgs)) {
|
||||
queue.songs = queue.songs.filter((item, index) => {
|
||||
if (songs.find((songIndex) => songIndex - 1 === index)) removed.push(item);
|
||||
else return true;
|
||||
});
|
||||
|
||||
interaction.reply(
|
||||
i18n.__mf("remove.result", {
|
||||
title: removed.map((song) => song.title).join("\n"),
|
||||
author: interaction.user.id
|
||||
})
|
||||
);
|
||||
} else if (!isNaN(+removeArgs) && +removeArgs >= 1 && +removeArgs <= queue.songs.length) {
|
||||
return interaction.reply(
|
||||
i18n.__mf("remove.result", {
|
||||
title: queue.songs.splice(+removeArgs - 1, 1)[0].title,
|
||||
author: interaction.user.id
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return interaction.reply({ content: i18n.__mf("remove.usageReply", { prefix: bot.prefix }) });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("resume").setDescription(i18n.__("resume.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("resume.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
if (queue.player.unpause()) {
|
||||
const content = i18n.__mf("resume.resultNotPlaying", { author: interaction.user.id });
|
||||
|
||||
safeReply(interaction, content);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const content = i18n.__("resume.errorPlaying");
|
||||
|
||||
safeReply(interaction, content);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ChatInputCommandInteraction,
|
||||
SlashCommandBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
StringSelectMenuInteraction
|
||||
} from "discord.js";
|
||||
import youtube, { Video } from "youtube-sr";
|
||||
import { bot } from "..";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("search")
|
||||
.setDescription(i18n.__("search.description"))
|
||||
.addStringOption((option) =>
|
||||
option.setName("query").setDescription(i18n.__("search.optionQuery")).setRequired(true)
|
||||
),
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
const query = interaction.options.getString("query", true);
|
||||
const member = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!member?.voice.channel)
|
||||
return interaction.reply({ content: i18n.__("search.errorNotChannel"), ephemeral: true }).catch(console.error);
|
||||
|
||||
const search = query;
|
||||
|
||||
await interaction.reply("⏳ Loading...").catch(console.error);
|
||||
|
||||
let results: Video[] = [];
|
||||
|
||||
try {
|
||||
results = await youtube.search(search, { limit: 10, type: "video" });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
interaction.editReply({ content: i18n.__("common.errorCommand") }).catch(console.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!results || !results[0]) {
|
||||
interaction.editReply({ content: i18n.__("search.noResults") });
|
||||
return;
|
||||
}
|
||||
|
||||
const options = results!.map((video) => {
|
||||
return {
|
||||
label: video.title ?? "",
|
||||
value: video.url
|
||||
};
|
||||
});
|
||||
|
||||
const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId("search-select")
|
||||
.setPlaceholder("Nothing selected")
|
||||
.setMinValues(1)
|
||||
.setMaxValues(10)
|
||||
.addOptions(options)
|
||||
);
|
||||
|
||||
const followUp = await interaction.followUp({
|
||||
content: "Choose songs to play",
|
||||
components: [row]
|
||||
});
|
||||
|
||||
followUp
|
||||
.awaitMessageComponent({
|
||||
time: 30000
|
||||
})
|
||||
.then((selectInteraction) => {
|
||||
if (!(selectInteraction instanceof StringSelectMenuInteraction)) return;
|
||||
|
||||
selectInteraction.update({ content: "⏳ Loading the selected songs...", components: [] });
|
||||
|
||||
bot.slashCommandsMap
|
||||
.get("play")!
|
||||
.execute(interaction, selectInteraction.values[0])
|
||||
.then(() => {
|
||||
selectInteraction.values.slice(1).forEach((url) => {
|
||||
bot.slashCommandsMap.get("play")!.execute(interaction, url);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("shuffle").setDescription(i18n.__("shuffle.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("shuffle.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!guildMemer || !canModifyQueue(guildMemer)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
let songs = queue.songs;
|
||||
|
||||
for (let i = songs.length - 1; i > 1; i--) {
|
||||
let j = 1 + Math.floor(Math.random() * i);
|
||||
[songs[i], songs[j]] = [songs[j], songs[i]];
|
||||
}
|
||||
|
||||
queue.songs = songs;
|
||||
|
||||
const content = i18n.__mf("shuffle.result", { author: interaction.user.id });
|
||||
|
||||
safeReply(interaction, content);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("skip").setDescription(i18n.__("skip.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!queue) return interaction.reply(i18n.__("skip.errorNotQueue")).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
queue.player.stop(true);
|
||||
|
||||
safeReply(interaction, i18n.__mf("skip.result", { author: interaction.user.id }));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("skipto")
|
||||
.setDescription(i18n.__("skipto.description"))
|
||||
.addIntegerOption((option) =>
|
||||
option.setName("number").setDescription(i18n.__("skipto.args.number")).setRequired(true)
|
||||
),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const playlistSlotArg = interaction.options.getInteger("number");
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!playlistSlotArg || isNaN(playlistSlotArg))
|
||||
return interaction
|
||||
.reply({
|
||||
content: i18n.__mf("skipto.usageReply", { prefix: bot.prefix, name: module.exports.name }),
|
||||
ephemeral: true
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("skipto.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
if (playlistSlotArg > queue.songs.length)
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("skipto.errorNotValid", { length: queue.songs.length }), ephemeral: true })
|
||||
.catch(console.error);
|
||||
|
||||
if (queue.loop) {
|
||||
for (let i = 0; i < playlistSlotArg - 2; i++) {
|
||||
queue.songs.push(queue.songs.shift()!);
|
||||
}
|
||||
} else {
|
||||
queue.songs = queue.songs.slice(playlistSlotArg - 2);
|
||||
}
|
||||
|
||||
queue.player.stop();
|
||||
|
||||
interaction
|
||||
.reply({ content: i18n.__mf("skipto.result", { author: interaction.user.id, arg: playlistSlotArg - 1 }) })
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
import { safeReply } from "../utils/safeReply";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("stop").setDescription(i18n.__("stop.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
|
||||
if (!queue) return interaction.reply(i18n.__("stop.errorNotQueue")).catch(console.error);
|
||||
if (!guildMemer || !canModifyQueue(guildMemer)) return i18n.__("common.errorNotChannel");
|
||||
|
||||
queue.stop();
|
||||
|
||||
safeReply(interaction, i18n.__mf("stop.result", { author: interaction.user.id }));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder().setName("uptime").setDescription(i18n.__("uptime.description")),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
let seconds = Math.floor(bot.client.uptime! / 1000);
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
let hours = Math.floor(minutes / 60);
|
||||
let days = Math.floor(hours / 24);
|
||||
|
||||
seconds %= 60;
|
||||
minutes %= 60;
|
||||
hours %= 24;
|
||||
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("uptime.result", { days: days, hours: hours, minutes: minutes, seconds: seconds }) })
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
import { bot } from "../index";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { canModifyQueue } from "../utils/queue";
|
||||
|
||||
export default {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("volume")
|
||||
.setDescription(i18n.__("volume.description"))
|
||||
.addIntegerOption((option) => option.setName("volume").setDescription(i18n.__("volume.description"))),
|
||||
execute(interaction: ChatInputCommandInteraction) {
|
||||
const queue = bot.queues.get(interaction.guild!.id);
|
||||
const guildMemer = interaction.guild!.members.cache.get(interaction.user.id);
|
||||
const volumeArg = interaction.options.getInteger("volume");
|
||||
|
||||
if (!queue)
|
||||
return interaction.reply({ content: i18n.__("volume.errorNotQueue"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!canModifyQueue(guildMemer!))
|
||||
return interaction.reply({ content: i18n.__("volume.errorNotChannel"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (!volumeArg || volumeArg === queue.volume)
|
||||
return interaction
|
||||
.reply({ content: i18n.__mf("volume.currentVolume", { volume: queue.volume }) })
|
||||
.catch(console.error);
|
||||
|
||||
if (isNaN(volumeArg))
|
||||
return interaction.reply({ content: i18n.__("volume.errorNotNumber"), ephemeral: true }).catch(console.error);
|
||||
|
||||
if (Number(volumeArg) > 100 || Number(volumeArg) < 0)
|
||||
return interaction.reply({ content: i18n.__("volume.errorNotValid"), ephemeral: true }).catch(console.error);
|
||||
|
||||
queue.volume = volumeArg;
|
||||
queue.resource.volume?.setVolumeLogarithmic(volumeArg / 100);
|
||||
|
||||
return interaction.reply({ content: i18n.__mf("volume.result", { arg: volumeArg }) }).catch(console.error);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user