diff --git a/src/commands/slashCommands/current.ts b/src/commands/slashCommands/current.ts new file mode 100644 index 0000000..4020052 --- /dev/null +++ b/src/commands/slashCommands/current.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { currentCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('current').setDescription('Get the current played song !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await currentCommand(guild.guildId, textChannel, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/index.ts b/src/commands/slashCommands/index.ts new file mode 100644 index 0000000..0493fe9 --- /dev/null +++ b/src/commands/slashCommands/index.ts @@ -0,0 +1,19 @@ +import * as current from './current'; +import * as pause from './pause'; +import * as play from './play'; +import * as queue from './queue'; +import * as redo from './redo'; +import * as remove from './remove'; +import * as resume from './resume'; +import * as skip from './skip'; + +export const commands = { + play, + skip, + remove, + pause, + resume, + queue, + current, + redo, +}; diff --git a/src/commands/slashCommands/pause.ts b/src/commands/slashCommands/pause.ts new file mode 100644 index 0000000..f85ae15 --- /dev/null +++ b/src/commands/slashCommands/pause.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { pauseCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('pause').setDescription('Pause the currently played song !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await pauseCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/play.ts b/src/commands/slashCommands/play.ts new file mode 100644 index 0000000..b896903 --- /dev/null +++ b/src/commands/slashCommands/play.ts @@ -0,0 +1,33 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { playCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder() + .setName('play') + .setDescription('Request a song !') + .addStringOption((option) => option.setName('query').setDescription('The search or youtube url !').setRequired(true)); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + //@ts-expect-error : getString is not a property of interaction.options + const query = interaction.options.getString('query'); + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + if (!query) return interaction.reply('Error: You must provide a query !'); + + await playCommand(['play', query], guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Song requested !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/queue.ts b/src/commands/slashCommands/queue.ts new file mode 100644 index 0000000..7561615 --- /dev/null +++ b/src/commands/slashCommands/queue.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { queueCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('queue').setDescription('Get the queue !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await queueCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/redo.ts b/src/commands/slashCommands/redo.ts new file mode 100644 index 0000000..dd11909 --- /dev/null +++ b/src/commands/slashCommands/redo.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { redoCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('redo').setDescription('Request the last song again !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await redoCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/remove.ts b/src/commands/slashCommands/remove.ts new file mode 100644 index 0000000..6c8f02d --- /dev/null +++ b/src/commands/slashCommands/remove.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { removeCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('remove').setDescription('Disconnect the bot !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await removeCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/resume.ts b/src/commands/slashCommands/resume.ts new file mode 100644 index 0000000..39f5ed4 --- /dev/null +++ b/src/commands/slashCommands/resume.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { resumeCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('resume').setDescription('Resume the currently played song !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await resumeCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/slashCommands/skip.ts b/src/commands/slashCommands/skip.ts new file mode 100644 index 0000000..9187d43 --- /dev/null +++ b/src/commands/slashCommands/skip.ts @@ -0,0 +1,27 @@ +import { CommandInteraction, SlashCommandBuilder, TextChannel } from 'discord.js'; +import { client } from 'src/Bot'; +import { fetchGuild } from 'src/database/queries/guilds/get'; + +import { skipCommand } from '../textCommands'; + +export const data = new SlashCommandBuilder().setName('skip').setDescription('Skip the currently played song !'); + +export async function execute(interaction: CommandInteraction) { + const guild = await fetchGuild(interaction.guildId!); + + const channel = client.channels.cache.get(interaction.channelId!); + if (!(channel instanceof TextChannel)) return interaction.reply('Error: Text channel not found !'); + + const textChannel = channel as TextChannel; + //@ts-expect-error : voiceChannel is not a property of interaction.member + const voiceChannel = interaction.member.voice.channel; + + if (!guild) return interaction.reply('Error: Guild not found !'); + if (!voiceChannel) return interaction.reply('Error: You must be in a voice channel !'); + if (!textChannel) return interaction.reply('Error: Text channel not found !'); + + await skipCommand(guild.guildId, textChannel, interaction.user, voiceChannel); + interaction.reply('Understood !'); + interaction.deleteReply(); + return; +} diff --git a/src/commands/textCommands.ts b/src/commands/textCommands.ts index 5326416..c7c93e7 100644 --- a/src/commands/textCommands.ts +++ b/src/commands/textCommands.ts @@ -43,7 +43,7 @@ export const playCommand = ( songRequest(request, guildId, requestAuthor, textChannel.id, voiceChannel); } else if (request.startsWith('https') && requestType === 'playlist') { playlistHandler(request, guildId, requestAuthor, textChannel.id, voiceChannel); - } else if (request.startsWith('https') && requestType === 'search') { + } else if (requestType === 'search') { searchSong(request, guildId, requestAuthor, textChannel.id, voiceChannel); } else { if (request.startsWith('https://www.youtube.com/live/')) { diff --git a/src/deploy-commands.ts b/src/deploy-commands.ts new file mode 100644 index 0000000..9528cea --- /dev/null +++ b/src/deploy-commands.ts @@ -0,0 +1,26 @@ +import { REST, Routes } from 'discord.js'; + +import { commands } from './commands/slashCommands'; +import { secrets } from './config/secrets'; + +type DeployCommandsProps = { + guildId: string; +}; + +const commandsData = Object.values(commands).map((command) => command.data); + +const rest = new REST({ version: '10' }).setToken(secrets.DISCORD_TOKEN); + +export async function deployCommands({ guildId }: DeployCommandsProps) { + try { + console.log('Started refreshing application (/) commands.'); + + await rest.put(Routes.applicationGuildCommands(secrets.DISCORD_CLIENT_ID.toString(), guildId), { + body: commandsData, + }); + + console.log('Successfully reloaded application (/) commands.'); + } catch (error) { + console.error(error); + } +} diff --git a/src/listeners/messageListener.ts b/src/listeners/messageListener.ts index b63b6ff..d2c161d 100644 --- a/src/listeners/messageListener.ts +++ b/src/listeners/messageListener.ts @@ -1,5 +1,6 @@ import { Client, Events } from 'discord.js'; import { yt_validate } from 'play-dl'; +import { commands } from 'src/commands/slashCommands'; import { currentCommand, pauseCommand, @@ -73,4 +74,14 @@ export default (client: Client): void => { message.delete(); }, 1000); }); + + client.on(Events.InteractionCreate, async (interaction) => { + if (!interaction.isCommand()) { + return; + } + const { commandName } = interaction; + if (commands[commandName as keyof typeof commands]) { + commands[commandName as keyof typeof commands].execute(interaction); + } + }); }; diff --git a/src/listeners/ready.ts b/src/listeners/ready.ts index 91b5eac..a3b7da8 100644 --- a/src/listeners/ready.ts +++ b/src/listeners/ready.ts @@ -1,5 +1,6 @@ import { Client, Events } from 'discord.js'; import { setActivity } from 'src/core/activityStatus'; +import { deployCommands } from 'src/deploy-commands'; export default (client: Client): void => { client.on(Events.ClientReady, async () => { @@ -9,5 +10,15 @@ export default (client: Client): void => { await setActivity(client); console.log(`${client.user.username} is online !`); + + const Guilds = client.guilds.cache.map((guild) => guild.id); + + for (const guild of Guilds) { + await deployCommands({ guildId: guild }); + } + }); + + client.on(Events.GuildCreate, async (guild) => { + await deployCommands({ guildId: guild.id }); }); };