diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 89a75d2..b385192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.3.1", "ffmpeg-static": "^5.2.0", "tweetnacl": "^1.0.3", + "youtube-sr": "^4.3.10", "ytdl-core": "^4.11.5" }, "devDependencies": { @@ -3015,6 +3016,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/youtube-sr": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/youtube-sr/-/youtube-sr-4.3.10.tgz", + "integrity": "sha512-YTpIWy2c1XLN4VpsUuZTDNXpJ2sLZQyG0kX1vq3nymHhDKro2SoeODey3pZazW+6AjfmNxoSnI8pCSzPrEa3jw==" + }, "node_modules/ytdl-core": { "version": "4.11.5", "resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.11.5.tgz", diff --git a/package.json b/package.json index c0ff75f..ed71679 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "dotenv": "^16.3.1", "ffmpeg-static": "^5.2.0", "tweetnacl": "^1.0.3", + "youtube-sr": "^4.3.10", "ytdl-core": "^4.11.5" }, "repository": { diff --git a/src/Events/onInteractionCreate.ts b/src/Events/onInteractionCreate.ts index 52edbc5..d7f30d0 100644 --- a/src/Events/onInteractionCreate.ts +++ b/src/Events/onInteractionCreate.ts @@ -3,9 +3,14 @@ import { interactions } from '../interactions'; import { BaseInteraction, Awaitable, ChannelType, GuildMember } from 'discord.js'; export async function onInteractionCreate(interaction: BaseInteraction): Promise> { + if (interaction.isStringSelectMenu()) { + interactions.searchplay(interaction); + return; + } if (!interaction.isChatInputCommand()) return; if (!interaction.guild) { interaction.reply({ content: 'This command can only be used in a server!', ephemeral: true }); + return; } const commandName = interaction.commandName; if (commandName === 'help') return interactions.help(interaction); @@ -53,6 +58,9 @@ export async function onInteractionCreate(interaction: BaseInteraction): Promise case 'nowplaying': interactions.nowplaying(interaction); break; + case 'search': + interactions.search(interaction); + break; default: interaction.reply(embeds.unknownCommand); break; diff --git a/src/Events/onMessageCreate.ts b/src/Events/onMessageCreate.ts index 008ba86..d1db9b1 100644 --- a/src/Events/onMessageCreate.ts +++ b/src/Events/onMessageCreate.ts @@ -8,7 +8,10 @@ export async function onMessageCreate(message: Message): Promise if (message.author.bot || !message.content.startsWith(prefix) || !message.guild) return; const args = message.content.slice(prefix.length).trim().split(/ +/) as string[]; const commandName = args.shift()?.toLowerCase(); - if (commandName === 'help' || commandName === 'h') return commands.help(message); + if (commandName === 'help' || commandName === 'h') { + commands.help(message); + return; + } const channel = message.member?.voice.channel; if (!channel) { message.reply(embeds.voiceChannelJoin); @@ -66,6 +69,9 @@ export async function onMessageCreate(message: Message): Promise case 'current': commands.nowplaying(message); break; + case 'search': + commands.search(message); + break; default: message.reply(embeds.unknownCommand); break; diff --git a/src/classes/player.ts b/src/classes/player.ts index b9f228c..06708b3 100644 --- a/src/classes/player.ts +++ b/src/classes/player.ts @@ -29,7 +29,7 @@ export class YTPlayer { }); this.player = createAudioPlayer(); this.queue = queueManager.getQueue(serverId) as Queue; - this.volume = 100 / 10; + this.volume = 0.1; this.player .on('subscribe', () => { this.isPlaying = true; @@ -52,7 +52,7 @@ export class YTPlayer { inputType: StreamType.WebmOpus, inlineVolume: true }); - this.resource.volume?.setVolume(0.1); + this.resource.volume?.setVolume(this.volume); this.connection.subscribe(this.player); this.player.play(this.resource); } @@ -68,6 +68,7 @@ export class YTPlayer { public stop(): void { this.connection.destroy(); queueManager.deleteQueue(this.serverId); + client.player = undefined; } public skip(): void { @@ -76,6 +77,7 @@ export class YTPlayer { public changeVolume(volume: number): void { if (!this.resource) return; + this.volume = volume; this.resource.volume?.setVolume(volume / 10); } diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 6965d37..1bb4dcc 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -5,12 +5,8 @@ import ytdl from 'ytdl-core'; export async function debugCommand(message: Message) { const url = message.content.split(' ')[1]; - if (!url) { - return message.reply(embeds.noUrl); - } - if (!ytdl.validateURL(url)) { - return message.reply(embeds.invaildUrl); - } + if (!url) return message.reply(embeds.noUrl); + if (!ytdl.validateURL(url)) return message.reply(embeds.invaildUrl); const info = await ytdl.getInfo(url); console.table(songResolver(info)); } diff --git a/src/commands/help.ts b/src/commands/help.ts index f911b48..f715799 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,6 +1,6 @@ import { embeds } from '../embeds'; import { Message } from 'discord.js'; -export async function helpCommand(message: Message): Promise { - message.reply(embeds.help); +export async function helpCommand(message: Message) { + return message.reply(embeds.help); } diff --git a/src/commands/index.ts b/src/commands/index.ts index 7cff44a..b6d3903 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -6,6 +6,7 @@ import { pauseCommand } from './pause'; import { playCommand } from './play'; import { queueCommand } from './queue'; import { resumeCommand } from './resume'; +import { searchCommand } from './search'; import { skipCommand } from './skip'; import { stopCommand } from './stop'; @@ -19,5 +20,6 @@ export const commands = { queue: queueCommand, resume: resumeCommand, skip: skipCommand, - stop: stopCommand + stop: stopCommand, + search: searchCommand }; diff --git a/src/commands/loop.ts b/src/commands/loop.ts index 8b6a0e1..40f63e0 100644 --- a/src/commands/loop.ts +++ b/src/commands/loop.ts @@ -5,7 +5,7 @@ import { Message } from 'discord.js'; export async function loopCommand(message: Message, args: string[]) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); const queue = queueManager.queues.get(message.guildId!) as Queue; args = args.filter((arg) => arg !== ''); if (args.length === 0) { diff --git a/src/commands/nowplaying.ts b/src/commands/nowplaying.ts index 15f0e7a..03afc05 100644 --- a/src/commands/nowplaying.ts +++ b/src/commands/nowplaying.ts @@ -6,7 +6,7 @@ import { Message } from 'discord.js'; export async function nowplayingCommand(message: Message) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); const queue = queueManager.getQueue(message.guild?.id as string) as Queue; const info = await getSongInfo(queue.currentSong); return message.reply(info); diff --git a/src/commands/pause.ts b/src/commands/pause.ts index 986ad15..05cb29d 100644 --- a/src/commands/pause.ts +++ b/src/commands/pause.ts @@ -5,7 +5,7 @@ import { Message } from 'discord.js'; export async function pauseCommand(message: Message) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); if (player.player.state.status === AudioPlayerStatus.Playing) { player.pause(); @@ -13,7 +13,5 @@ export async function pauseCommand(message: Message) { } else if (player.player.state.status === AudioPlayerStatus.Paused) { player.resume(); message.reply(embeds.videoResumed); - } else { - message.reply(embeds.videoNotPlaying); - } + } else message.reply(embeds.videoNotPlaying); } diff --git a/src/commands/play.ts b/src/commands/play.ts index 38d635e..5da3c87 100644 --- a/src/commands/play.ts +++ b/src/commands/play.ts @@ -9,11 +9,11 @@ let url: string; export async function playCommand(message: Message) { let player = client?.player; - if (typeof queueManager.getQueue(message.guild?.id as string) === 'undefined') { + if (!queueManager.getQueue(message.guild?.id as string)) { queueManager.setQueue(message.guild?.id as string, new Queue()); } const queue = queueManager.getQueue(message.guild?.id as string) as Queue; - if (typeof player === 'undefined') { + if (!player) { client.player = new YTPlayer( message.guild?.id as string, message.member?.voice.channel as VoiceBasedChannel, diff --git a/src/commands/queue.ts b/src/commands/queue.ts index 3a102f4..b5aaeae 100644 --- a/src/commands/queue.ts +++ b/src/commands/queue.ts @@ -7,7 +7,7 @@ import ytdl from 'ytdl-core'; export async function queueCommand(message: Message) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); const queue = queueManager.getQueue(message.guildId!) as Queue; const embed = new embeds.embed().setTitle('Queue').setColor('Blue').setTimestamp(); diff --git a/src/commands/resume.ts b/src/commands/resume.ts index 21e9cd8..3e514e6 100644 --- a/src/commands/resume.ts +++ b/src/commands/resume.ts @@ -5,13 +5,11 @@ import { Message } from 'discord.js'; export async function resumeCommand(message: Message) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); if (player.player.state.status === AudioPlayerStatus.Paused) { player.resume(); message.reply(embeds.videoResumed); } else if (player.player.state.status === AudioPlayerStatus.Playing) { message.reply(embeds.videoNotPaused); - } else { - message.reply(embeds.videoNotPlaying); - } + } else message.reply(embeds.videoNotPlaying); } diff --git a/src/commands/search.ts b/src/commands/search.ts new file mode 100644 index 0000000..a7364a5 --- /dev/null +++ b/src/commands/search.ts @@ -0,0 +1,28 @@ +import { embeds } from '../embeds'; +import { Message, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ActionRowBuilder } from 'discord.js'; +import ytsr from 'youtube-sr'; + +export async function searchCommand(message: Message) { + const videos = await ytsr.search(message.content.split(' ').slice(1).join(' '), { + type: 'video' + }); + + if (!videos.length) return message.reply(embeds.noResult); + + const menu = new StringSelectMenuBuilder() + .setCustomId('search') + .setPlaceholder('Select a video') + .addOptions( + videos.map((video, index) => + new StringSelectMenuOptionBuilder() + .setLabel(`${index + 1}. ${video.title}`) + .setValue(video.url) + .setDescription(String(video.durationFormatted)) + .setEmoji('๐ŸŽต') + ) + ); + + const row = new ActionRowBuilder().addComponents(menu); + + message.reply({ content: 'Select a video', components: [row] }); +} diff --git a/src/commands/skip.ts b/src/commands/skip.ts index f60aaa9..d6aac50 100644 --- a/src/commands/skip.ts +++ b/src/commands/skip.ts @@ -4,7 +4,7 @@ import { Message } from 'discord.js'; export async function skipCommand(message: Message) { const player = client?.player; - if (typeof player === 'undefined') return message.reply(embeds.videoNotPlaying); + if (!player) return message.reply(embeds.videoNotPlaying); player.skip(); return message.reply(embeds.videoNext); } diff --git a/src/commands/stop.ts b/src/commands/stop.ts index 5e8965f..5b59308 100644 --- a/src/commands/stop.ts +++ b/src/commands/stop.ts @@ -8,9 +8,7 @@ export async function stopCommand(message: Message): Promise { message.guild.members.me.voice.disconnect(); client.player = undefined; message.reply(embeds.videoStopped); - } else { - message.reply(embeds.videoNotPlaying); - } + } else message.reply(embeds.videoNotPlaying); queueManager.queues.delete(message.guildId!); } diff --git a/src/deploy-commands.ts b/src/deploy-commands.ts index e65a53d..da8e66e 100644 --- a/src/deploy-commands.ts +++ b/src/deploy-commands.ts @@ -8,6 +8,10 @@ const commands = [ .setName('play') .setDescription('Play a song') .addStringOption((option) => option.setName('url').setDescription('The song to play').setRequired(true)), + new SlashCommandBuilder() + .setName('search') + .setDescription('Search for a song') + .addStringOption((option) => option.setName('query').setDescription('Enter search words').setRequired(true)), new SlashCommandBuilder().setName('pause').setDescription('Pause the current song'), new SlashCommandBuilder().setName('resume').setDescription('Resume the current song'), new SlashCommandBuilder().setName('skip').setDescription('Skip the current song'), diff --git a/src/embeds/index.ts b/src/embeds/index.ts index 19d3751..b2a56bc 100644 --- a/src/embeds/index.ts +++ b/src/embeds/index.ts @@ -15,7 +15,7 @@ export const embeds = { { name: 'pause', value: 'ๅ†็”Ÿใ‚’ไธ€ๆ™‚ๅœๆญข/ๅ†้–‹ใ—ใพใ™ใ€‚' }, { name: 'resume', value: 'ๅ†็”Ÿใ‚’ๅ†้–‹ใ—ใพใ™ใ€‚' }, { name: 'nowplaying', value: '็พๅœจใฎๆ›ฒใ‚’่กจ็คบใ—ใพใ™ใ€‚' }, - { name: 'volume', value: '้Ÿณ้‡ใ‚’ๅค‰ๆ›ดใ—ใพใ™ใ€‚'} + { name: 'volume', value: '้Ÿณ้‡ใ‚’ๅค‰ๆ›ดใ—ใพใ™ใ€‚' } ) .build(), videoNotPlaying: new Builder() @@ -38,6 +38,10 @@ export const embeds = { .addFields({ name: 'Error', value: 'ๆœ‰ๅŠนใชURLใ‚’ๆŒ‡ๅฎšใ—ใฆใใ ใ•ใ„ใ€‚' }) .setColor('Red') .build(), + noResult: new Builder() + .addFields({ name: 'Error', value: 'ๆคœ็ดข็ตๆžœใŒ่ฆ‹ใคใ‹ใ‚Šใพใ›ใ‚“ใงใ—ใŸใ€‚' }) + .setColor('Red') + .build(), voiceChannnelPermission: new Builder() .addFields({ name: 'Error', value: 'ใƒœใ‚คใ‚นใƒใƒฃใƒณใƒใƒซใซๅ‚ๅŠ ใ™ใ‚‹ๆจฉ้™ใŒใ‚ใ‚Šใพใ›ใ‚“ใ€‚' }) .setColor('Red') diff --git a/src/interactions/changeVolume.ts b/src/interactions/changeVolume.ts index 1092916..d6f3621 100644 --- a/src/interactions/changeVolume.ts +++ b/src/interactions/changeVolume.ts @@ -7,7 +7,7 @@ export async function changeVolumeCommand(interaction: ChatInputCommandInteracti if (!player) return interaction.reply(embeds.videoNotPlaying); const vol = interaction.options.getInteger('volume'); - if (!vol) return interaction.reply({ content: `็พๅœจใฎ้Ÿณ้‡ใฏ${player.volume}ใงใ™ใ€‚` }); + if (!vol) return interaction.reply({ content: `็พๅœจใฎ้Ÿณ้‡ใฏ${player.volume * 10}ใงใ™ใ€‚` }); if (vol >= 100) { player.changeVolume(100 / 10); return interaction.reply({ content: 'ใƒœใƒชใƒฅใƒผใƒ ใ‚’ๆœ€ๅคงใซ่จญๅฎšใ—ใพใ—ใŸใ€‚' }); diff --git a/src/interactions/handleplay.ts b/src/interactions/handleplay.ts new file mode 100644 index 0000000..c89924c --- /dev/null +++ b/src/interactions/handleplay.ts @@ -0,0 +1,68 @@ +import { YTPlayer } from '../classes/player'; +import { Queue, queueManager } from '../classes/queue'; +import { embeds } from '../embeds'; +import { client } from '../index'; +import { StringSelectMenuInteraction, ChannelType, VoiceBasedChannel, GuildMember } from 'discord.js'; +import ytdl from 'ytdl-core'; + +let url: string; + +export async function searchPlayCommand(interaction: StringSelectMenuInteraction) { + let player = client?.player; + if (!queueManager.getQueue(interaction.guild?.id as string)) { + queueManager.setQueue(interaction.guild?.id as string, new Queue()); + } + const queue = queueManager.getQueue(interaction.guild?.id as string) as Queue; + if (!interaction.channel) return; + if (!(interaction.member instanceof GuildMember)) return; + if (!player) { + client.player = new YTPlayer( + interaction.guild?.id as string, + interaction.member?.voice.channel as VoiceBasedChannel, + interaction.channel?.id + ); + player = client.player; + } + + url = interaction.values[0]; + const channel = interaction.member?.voice.channel; + if (!url) return interaction.reply(embeds.noUrl); + if (!ytdl.validateURL(url)) return interaction.reply(embeds.invaildUrl); + if (!channel) return interaction.reply(embeds.voiceChannelJoin); + if (channel.type !== ChannelType.GuildVoice) return; + if (!channel.joinable) return interaction.reply(embeds.voiceChannnelJoined); + if (!channel.speakable) return interaction.reply(embeds.voiceChannnelPermission); + + if (!queue.length || !player.isPlaying) { + queue.addSong(url); + const info = await ytdl.getInfo(url); + interaction.reply( + new embeds.embed() + .setTitle('Success') + .setDescription(`**[${info.videoDetails.title}](${info.videoDetails.video_url})ใ‚’ๅ†็”Ÿใ—ใพใ™ใ€‚**`) + .addFields({ + name: info.videoDetails.title, + value: `ๆŠ•็จฟ่€…: [${info.videoDetails.author.name}](${info.videoDetails.author.channel_url})` + }) + .setImage(info.videoDetails.thumbnails[0].url.split('?')[0]) + .setColor('Green') + .build() + ); + if (queue.length === 1) return player.play(); + } else { + queue.addSong(url); + const info = await ytdl.getInfo(url); + interaction.reply( + new embeds.embed() + .setTitle('Info') + .setDescription(`**[${info.videoDetails.title}](${info.videoDetails.video_url})ใ‚’ใ‚ญใƒฅใƒผใซ่ฟฝๅŠ ใ—ใพใ—ใŸใ€‚**`) + .addFields({ + name: info.videoDetails.title, + value: `ๆŠ•็จฟ่€…: [${info.videoDetails.author.name}](${info.videoDetails.author.channel_url})` + }) + .setImage(info.videoDetails.thumbnails[0].url.split('?')[0]) + .setColor('Yellow') + .build() + ); + } +} diff --git a/src/interactions/help.ts b/src/interactions/help.ts index 7b04714..73871a1 100644 --- a/src/interactions/help.ts +++ b/src/interactions/help.ts @@ -1,6 +1,6 @@ import { embeds } from '../embeds'; import { ChatInputCommandInteraction } from 'discord.js'; -export async function helpCommand(interaction: ChatInputCommandInteraction): Promise { +export async function helpCommand(interaction: ChatInputCommandInteraction) { interaction.reply(embeds.help); } diff --git a/src/interactions/index.ts b/src/interactions/index.ts index 2435020..d9edbf4 100644 --- a/src/interactions/index.ts +++ b/src/interactions/index.ts @@ -1,4 +1,5 @@ import { changeVolumeCommand } from './changeVolume'; +import { searchPlayCommand } from './handleplay'; import { helpCommand } from './help'; import { loopCommand } from './loop'; import { nowplayingCommand } from './nowplaying'; @@ -6,6 +7,7 @@ import { pauseCommand } from './pause'; import { playCommand } from './play'; import { queueCommand } from './queue'; import { resumeCommand } from './resume'; +import { searchCommand } from './search'; import { skipCommand } from './skip'; import { stopCommand } from './stop'; @@ -19,5 +21,7 @@ export const interactions = { queue: queueCommand, resume: resumeCommand, skip: skipCommand, - stop: stopCommand + stop: stopCommand, + search: searchCommand, + searchplay: searchPlayCommand }; diff --git a/src/interactions/loop.ts b/src/interactions/loop.ts index ec7636d..087b924 100644 --- a/src/interactions/loop.ts +++ b/src/interactions/loop.ts @@ -5,7 +5,7 @@ import { ChatInputCommandInteraction } from 'discord.js'; export async function loopCommand(interaction: ChatInputCommandInteraction) { const player = client?.player; - if (typeof player === 'undefined') return interaction.reply(embeds.videoNotPlaying); + if (!player) return interaction.reply(embeds.videoNotPlaying); const queue = queueManager.queues.get(interaction.guildId!) as Queue; const settings = interaction.options.getString('mode') as string; if (!settings) { diff --git a/src/interactions/nowplaying.ts b/src/interactions/nowplaying.ts index 7934402..49f21d5 100644 --- a/src/interactions/nowplaying.ts +++ b/src/interactions/nowplaying.ts @@ -7,7 +7,7 @@ import { ChatInputCommandInteraction } from 'discord.js'; export async function nowplayingCommand(interaction: ChatInputCommandInteraction) { await interaction.deferReply(); const player = client?.player; - if (typeof player === 'undefined') return interaction.followUp(embeds.videoNotPlaying); + if (!player) return interaction.followUp(embeds.videoNotPlaying); const queue = queueManager.getQueue(interaction.guild?.id as string) as Queue; const info = await getSongInfo(queue.currentSong); return interaction.followUp(info); diff --git a/src/interactions/pause.ts b/src/interactions/pause.ts index bea9980..5d62ab7 100644 --- a/src/interactions/pause.ts +++ b/src/interactions/pause.ts @@ -5,7 +5,7 @@ import { ChatInputCommandInteraction } from 'discord.js'; export async function pauseCommand(interaction: ChatInputCommandInteraction) { const player = client?.player; - if (typeof player === 'undefined') return interaction.reply(embeds.videoNotPlaying); + if (!player) return interaction.reply(embeds.videoNotPlaying); if (player.player.state.status === AudioPlayerStatus.Playing) { player.pause(); @@ -13,7 +13,5 @@ export async function pauseCommand(interaction: ChatInputCommandInteraction) { } else if (player.player.state.status === AudioPlayerStatus.Paused) { player.resume(); interaction.reply(embeds.videoResumed); - } else { - interaction.reply(embeds.videoNotPlaying); - } + } else interaction.reply(embeds.videoNotPlaying); } diff --git a/src/interactions/play.ts b/src/interactions/play.ts index 5778632..cef7b9c 100644 --- a/src/interactions/play.ts +++ b/src/interactions/play.ts @@ -9,13 +9,13 @@ let url: string; export async function playCommand(interaction: ChatInputCommandInteraction) { let player = client?.player; - if (typeof queueManager.getQueue(interaction.guild?.id as string) === 'undefined') { + if (!queueManager.getQueue(interaction.guild?.id as string)) { queueManager.setQueue(interaction.guild?.id as string, new Queue()); } const queue = queueManager.getQueue(interaction.guild?.id as string) as Queue; if (!interaction.channel) return; if (!(interaction.member instanceof GuildMember)) return; - if (typeof player === 'undefined') { + if (!player) { client.player = new YTPlayer( interaction.guild?.id as string, interaction.member?.voice.channel as VoiceBasedChannel, diff --git a/src/interactions/queue.ts b/src/interactions/queue.ts index 72c5830..77271c2 100644 --- a/src/interactions/queue.ts +++ b/src/interactions/queue.ts @@ -8,7 +8,7 @@ import ytdl from 'ytdl-core'; export async function queueCommand(interaction: ChatInputCommandInteraction) { const player = client?.player; await interaction.deferReply(); - if (typeof player === 'undefined') return interaction.followUp(embeds.videoNotPlaying); + if (!player) return interaction.followUp(embeds.videoNotPlaying); const queue = queueManager.getQueue(interaction.guildId!) as Queue; const embed = new embeds.embed().setTitle('Queue').setColor('Blue').setTimestamp(); diff --git a/src/interactions/resume.ts b/src/interactions/resume.ts index 50c0703..7af6de3 100644 --- a/src/interactions/resume.ts +++ b/src/interactions/resume.ts @@ -11,7 +11,5 @@ export async function resumeCommand(interaction: ChatInputCommandInteraction) { interaction.reply(embeds.videoResumed); } else if (player.player.state.status === AudioPlayerStatus.Playing) { interaction.reply(embeds.videoNotPaused); - } else { - interaction.reply(embeds.videoNotPlaying); - } + } else interaction.reply(embeds.videoNotPlaying); } diff --git a/src/interactions/search.ts b/src/interactions/search.ts new file mode 100644 index 0000000..523dc78 --- /dev/null +++ b/src/interactions/search.ts @@ -0,0 +1,34 @@ +import { embeds } from '../embeds'; +import { + ChatInputCommandInteraction, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, + ActionRowBuilder +} from 'discord.js'; +import ytsr from 'youtube-sr'; + +export async function searchCommand(interaction: ChatInputCommandInteraction) { + interaction.deferReply(); + const videos = await ytsr.search(interaction.options.getString('query') as string, { + type: 'video' + }); + + if (!videos.length) return interaction.followUp(embeds.noResult); + + const menu = new StringSelectMenuBuilder() + .setCustomId('search') + .setPlaceholder('Select a video') + .addOptions( + videos.map((video, index) => + new StringSelectMenuOptionBuilder() + .setLabel(`${index + 1}. ${video.title}`) + .setValue(video.url) + .setDescription(String(video.durationFormatted)) + .setEmoji('๐ŸŽต') + ) + ); + + const row = new ActionRowBuilder().addComponents(menu); + + interaction.followUp({ content: 'Select a video', components: [row] }); +} diff --git a/src/interactions/skip.ts b/src/interactions/skip.ts index 39872d7..3056b95 100644 --- a/src/interactions/skip.ts +++ b/src/interactions/skip.ts @@ -4,7 +4,7 @@ import { ChatInputCommandInteraction } from 'discord.js'; export async function skipCommand(interaction: ChatInputCommandInteraction) { const player = client?.player; - if (typeof player === 'undefined') return interaction.reply(embeds.videoNotPlaying); + if (!player) return interaction.reply(embeds.videoNotPlaying); player.skip(); return interaction.reply(embeds.videoNext); } diff --git a/src/interactions/stop.ts b/src/interactions/stop.ts index 39af13c..3890016 100644 --- a/src/interactions/stop.ts +++ b/src/interactions/stop.ts @@ -8,9 +8,7 @@ export async function stopCommand(interaction: ChatInputCommandInteraction): Pro interaction.guild.members.me.voice.disconnect(); client.player = undefined; interaction.reply(embeds.videoStopped); - } else { - interaction.reply(embeds.videoNotPlaying); - } + } else interaction.reply(embeds.videoNotPlaying); queueManager.queues.delete(interaction.guildId!); }