diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..0ebed05 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,36 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es2021: true, + }, + extends: 'airbnb-base', + overrides: [{ + env: { + node: true, + }, + files: [ + '.eslintrc.{js,cjs}', + ], + parserOptions: { + sourceType: 'script', + }, + }, + ], + parserOptions: { + ecmaVersion: 'latest', + }, + rules: { + // "indent": ["error", { "allowIndentationTabs": true }], + // "allowIndentationTabs": true, + // 'no-tabs': 0, + indent: ['error', 'tab'], + 'no-tabs': ['error', { allowIndentationTabs: true }], + 'no-multi-spaces': 'error', + 'object-curly-newline': 'off', + 'no-trailing-spaces': 'error', + 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }], + 'no-duplicate-imports': ['error', { includeExports: true }], + 'eol-last': ['error', 'never'], + }, +}; \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4aa884e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "prettier.useTabs": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + }, + "eslint.validate": ["javascript"] + + // "editor.formatOnSave": true, + // "eslint.format.enable": true +} \ No newline at end of file diff --git a/commands/loop.js b/commands/loop.js index ec3053a..03983f1 100644 --- a/commands/loop.js +++ b/commands/loop.js @@ -1,34 +1,34 @@ -const musicQueue = require("../musicQueue"); +const musicQueue = require('../musicQueue'); async function enableLooping(interaction) { - await interaction.deferReply(); - const guildId = interaction.guild.id; - - musicQueue.enableLooping(guildId); - interaction.followUp("Enabled looping for the current queue."); + await interaction.deferReply(); + const guildId = interaction.guild.id; + + musicQueue.enableLooping(guildId); + interaction.followUp('Enabled looping for the current queue.'); } async function unloopCommand(interaction) { - await interaction.deferReply(); - const guildId = interaction.guild.id; + await interaction.deferReply(); + const guildId = interaction.guild.id; - musicQueue.disableLooping(guildId); - interaction.followUp("Disabled looping for the current queue."); + musicQueue.disableLooping(guildId); + interaction.followUp('Disabled looping for the current queue.'); } async function toggleLoopCommand(interaction) { - await interaction.deferReply(); - const guildId = interaction.guild.id; + await interaction.deferReply(); + const guildId = interaction.guild.id; - if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) { - musicQueue.disableLooping(guildId, false); - interaction.followUp("Disabled looping for the current queue."); - } else { - musicQueue.enableLooping(guildId, true); - interaction.followUp("Enabled looping for the current queue."); - } + if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) { + musicQueue.disableLooping(guildId, false); + interaction.followUp('Disabled looping for the current queue.'); + } else { + musicQueue.enableLooping(guildId, true); + interaction.followUp('Enabled looping for the current queue.'); + } } module.exports.toggleLoopCommand = toggleLoopCommand; module.exports.unloopCommand = unloopCommand; -module.exports.enableLooping = enableLooping; +module.exports.enableLooping = enableLooping; \ No newline at end of file diff --git a/commands/pauseResume.js b/commands/pauseResume.js index baabf41..14acfc2 100644 --- a/commands/pauseResume.js +++ b/commands/pauseResume.js @@ -1,34 +1,34 @@ -const { getVoiceConnection } = require("@discordjs/voice"); +const { getVoiceConnection } = require('@discordjs/voice'); async function pauseCommand(interaction) { - await interaction.deferReply(); + await interaction.deferReply(); - const connection = getVoiceConnection(interaction.guild.id); + const connection = getVoiceConnection(interaction.guild.id); - if (!connection) { - return interaction.followUp( - "There is no active music player in this server." - ); - } + if (!connection) { + return interaction.followUp( + 'There is no active music player in this server.', + ); + } - connection.state.subscription.player.pause(); - interaction.followUp("Paused the music."); + connection.state.subscription.player.pause(); + return interaction.followUp('Paused the music.'); } async function unpauseCommand(interaction) { - await interaction.deferReply(); + await interaction.deferReply(); - const connection = getVoiceConnection(interaction.guild.id); + const connection = getVoiceConnection(interaction.guild.id); - if (!connection) { - return interaction.followUp( - "There is no active music player in this server." - ); - } + if (!connection) { + return interaction.followUp( + 'There is no active music player in this server.', + ); + } - connection.state.subscription.player.unpause(); - interaction.followUp("Resumed the music."); + connection.state.subscription.player.unpause(); + return interaction.followUp('Resumed the music.'); } module.exports.pauseCommand = pauseCommand; -module.exports.unpauseCommand = unpauseCommand; +module.exports.unpauseCommand = unpauseCommand; \ No newline at end of file diff --git a/commands/play.js b/commands/play.js index a65ca59..92cb15b 100644 --- a/commands/play.js +++ b/commands/play.js @@ -1,32 +1,41 @@ -const { getMusicStream } = require("./../utils/getMusicStream"); -const musicQueue = require("../musicQueue"); -const { musicPlayer } = require("../utils/musicPlayer"); -const { joinVoiceChannel } = require("@discordjs/voice"); +/* eslint-disable consistent-return */ +const { joinVoiceChannel } = require('@discordjs/voice'); +const { getMusicStream } = require('../utils/getMusicStream'); +const musicQueue = require('../musicQueue'); +const { musicPlayer } = require('../utils/musicPlayer'); async function playCommand(interaction) { - await interaction.deferReply(); + await interaction.deferReply(); - const query = interaction.options.getString("input"); - const voiceChannel = interaction.member.voice.channel; + const query = interaction.options.getString('input'); + const voiceChannel = interaction.member.voice.channel; - if (!voiceChannel) { - return interaction.followUp( - "You must be in a voice channel to use this command." - ); - } - const song = await getMusicStream(query); + if (!voiceChannel) { + return interaction.followUp( + 'You must be in a voice channel to use this command.', + ); + } + const song = await getMusicStream(query); - musicQueue.addToQueue(interaction.guild.id, song); + const connection = joinVoiceChannel({ + channelId: voiceChannel.id, + guildId: interaction.guild.id, + adapterCreator: interaction.guild.voiceAdapterCreator, + selfDeaf: false, + selfMute: false, + }); - const connection = joinVoiceChannel({ - channelId: voiceChannel.id, - guildId: interaction.guild.id, - adapterCreator: interaction.guild.voiceAdapterCreator, - selfDeaf: false, - selfMute: false, - }); + if (musicQueue.getQueue(interaction.guild.id).length > 0) { + musicQueue.removeFromQueue(interaction.guild.id); + } - musicPlayer(interaction.guild.id, connection, interaction); + musicQueue.addToQueue(interaction.guild.id, song); + + musicPlayer( + interaction.guild.id, + connection, + interaction, + ); } -module.exports.playCommand = playCommand; +module.exports.playCommand = playCommand; \ No newline at end of file diff --git a/commands/queue.js b/commands/queue.js index feb99e1..e22bd8f 100644 --- a/commands/queue.js +++ b/commands/queue.js @@ -1,27 +1,27 @@ -const musicQueue = require("../musicQueue"); -const { getMusicStream } = require("../utils/getMusicStream"); +const musicQueue = require('../musicQueue'); +const { getMusicStream } = require('../utils/getMusicStream'); async function queueCommand(interaction) { - await interaction.deferReply(); + await interaction.deferReply(); - const query = interaction.options.getString("song"); - const voiceChannel = interaction.member.voice.channel; + const query = interaction.options.getString('song'); + const voiceChannel = interaction.member.voice.channel; - if (!voiceChannel) { - return interaction.followUp( - "You must be in a voice channel to use this command." - ); - } + if (!voiceChannel) { + return interaction.followUp( + 'You must be in a voice channel to use this command.', + ); + } - const song = await getMusicStream(query); - if (!song) { - return interaction - .followUp("Error finding song. Try Again.") - .then((msg) => setTimeout(() => msg.delete(), 10000)); - } + const song = await getMusicStream(query); + if (!song) { + return interaction + .followUp('Error finding song. Try Again.') + .then((msg) => setTimeout(() => msg.delete(), 10000)); + } - musicQueue.addToQueue(interaction.guild.id, song); - interaction.followUp(`Added ${song.title} to the queue.`); + musicQueue.addToQueue(interaction.guild.id, song); + return interaction.followUp(`Added ${song.title} to the queue.`); } -module.exports.queueCommand = queueCommand; +module.exports.queueCommand = queueCommand; \ No newline at end of file diff --git a/commands/stop.js b/commands/stop.js index 7a99251..483a38c 100644 --- a/commands/stop.js +++ b/commands/stop.js @@ -1,30 +1,30 @@ -const musicQueue = require("../musicQueue"); -const { getVoiceConnection } = require("@discordjs/voice"); +const { getVoiceConnection } = require('@discordjs/voice'); +const musicQueue = require('../musicQueue'); async function stopCommand(interaction) { - await interaction.deferReply(); + await interaction.deferReply(); - const voiceChannel = interaction.member.voice.channel; - const connection = getVoiceConnection(interaction.guild.id); + const voiceChannel = interaction.member.voice.channel; + const connection = getVoiceConnection(interaction.guild.id); - if (!voiceChannel) { - return interaction.followUp( - "You must be in a voice channel to use this command." - ); - } + if (!voiceChannel) { + return interaction.followUp( + 'You must be in a voice channel to use this command.', + ); + } - const guildId = interaction.guild.id; + const guildId = interaction.guild.id; - if (!connection.state.subscription.player) { - return interaction.followUp( - "I am not currently playing music in a voice channel." - ); - } + if (!connection.state.subscription.player) { + return interaction.followUp( + 'I am not currently playing music in a voice channel.', + ); + } - connection.state.subscription.player.stop(); - musicQueue.clearQueue(guildId); + connection.state.subscription.player.stop(); + musicQueue.clearQueue(guildId); - interaction.followUp("Stopped the music and cleared the queue."); + return interaction.followUp('Stopped the music and cleared the queue.'); } -module.exports.stopCommand = stopCommand; +module.exports.stopCommand = stopCommand; \ No newline at end of file diff --git a/index.js b/index.js index aeb1f9d..6bcee7d 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ const { Client, GatewayIntentBits } = require('discord.js'); +const process = require('dotenv').config(); const { registerCommands } = require('./utils/registerCommands'); const { playCommand } = require('./commands/play'); const { queueCommand } = require('./commands/queue'); @@ -7,50 +8,49 @@ const { pauseCommand, unpauseCommand } = require('./commands/pauseResume'); const { toggleLoopCommand } = require('./commands/loop'); const { ReAuth } = require('./reAuthenticate'); -const process = require('dotenv').config(); -const clientId = process.parsed.clientId; -const token = process.parsed.token; +const { clientId } = process.parsed; +const { token } = process.parsed; -const client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent, - GatewayIntentBits.GuildVoiceStates, - GatewayIntentBits.GuildIntegrations - ] -}) +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildVoiceStates, + GatewayIntentBits.GuildIntegrations, + ], +}); client.on('ready', async () => { - console.log(`Logged in as ${client.user.tag}!`); - - await registerCommands(clientId, token); + console.log(`Logged in as ${client.user.tag}!`); + + await registerCommands(clientId, token); }); client.on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; + if (!interaction.isCommand()) return; - const { commandName } = interaction; + const { commandName } = interaction; - if (commandName === 'play') { - await playCommand(interaction); - } else if (commandName === 'queue') { - await queueCommand(interaction); - } else if (commandName === 'pause') { - await pauseCommand(interaction); - } else if (commandName === 'resume') { - await unpauseCommand(interaction); - } else if (commandName === 'loop') { - await toggleLoopCommand(interaction); - } else if (commandName === 'stop') { - await stopCommand(interaction); - } + if (commandName === 'play') { + await playCommand(interaction, client); + } else if (commandName === 'queue') { + await queueCommand(interaction); + } else if (commandName === 'pause') { + await pauseCommand(interaction); + } else if (commandName === 'resume') { + await unpauseCommand(interaction); + } else if (commandName === 'loop') { + await toggleLoopCommand(interaction); + } else if (commandName === 'stop') { + await stopCommand(interaction); + } }); - + client.on('messageCreate', async (message) => { - if(message.content == 'reauth') { - await ReAuth(); - } + if (message.content === 'reauth') { + await ReAuth(); + } }); client.login(token); diff --git a/musicQueue.js b/musicQueue.js index a1fbbc8..c56d07d 100644 --- a/musicQueue.js +++ b/musicQueue.js @@ -1,58 +1,58 @@ const { getMusicStream } = require('./utils/getMusicStream'); class MusicQueue { - constructor() { - this.queue = new Map(); - this.looping = new Map(); - } + constructor() { + this.queue = new Map(); + this.looping = new Map(); + } - addToQueue(guildId, song) { - if (!this.queue.has(guildId)) { - this.queue.set(guildId, []); - } + addToQueue(guildId, song) { + if (!this.queue.has(guildId)) { + this.queue.set(guildId, []); + } - this.queue.get(guildId).push(song); - } + this.queue.get(guildId).push(song); + } - async removeFromQueue(guildId) { - if (!this.queue.has(guildId)) { - return; - } + async removeFromQueue(guildId) { + if (!this.queue.has(guildId)) { + return; + } - const serverQueue = this.queue.get(guildId); + const serverQueue = this.queue.get(guildId); - if (this.looping.has(guildId) && this.looping.get(guildId)) { - const song = serverQueue.shift(); - const newSong = await getMusicStream(song.userInput); - serverQueue.push(newSong); - } else { - serverQueue.shift(); - } - } + if (this.looping.has(guildId) && this.looping.get(guildId)) { + const song = serverQueue.shift(); + const newSong = await getMusicStream(song.userInput); + serverQueue.push(newSong); + } else { + serverQueue.shift(); + } + } - getQueue(guildId) { - if (!this.queue.has(guildId)) { - return []; - } + getQueue(guildId) { + if (!this.queue.has(guildId)) { + return []; + } - return this.queue.get(guildId); - } + return this.queue.get(guildId); + } - enableLooping(guildId) { - this.looping.set(guildId, true); - } + enableLooping(guildId) { + this.looping.set(guildId, true); + } - disableLooping(guildId) { - this.looping.set(guildId, false); - } + disableLooping(guildId) { + this.looping.set(guildId, false); + } - clearQueue(guildId) { - if (!this.queue.has(guildId)) { - return; - } + clearQueue(guildId) { + if (!this.queue.has(guildId)) { + return; + } - this.queue.set(guildId, []); - } + this.queue.set(guildId, []); + } } module.exports = new MusicQueue(); \ No newline at end of file diff --git a/music_sources/soundcloud.js b/music_sources/soundcloud.js index ab44b6f..7af9975 100644 --- a/music_sources/soundcloud.js +++ b/music_sources/soundcloud.js @@ -1,14 +1,14 @@ -const playdl = require("play-dl"); +const playdl = require('play-dl'); async function getStream(query) { - let songInformation = await playdl.soundcloud(query); - let stream = await playdl.stream_from_info(songInformation, { quality: 2 }); - return { - title: songInformation.name, - stream: stream.stream, - duration: songInformation.durationInSec * 1000, - userInput: query, - }; + const songInformation = await playdl.soundcloud(query); + const stream = await playdl.stream_from_info(songInformation, { quality: 2 }); + return { + title: songInformation.name, + stream: stream.stream, + duration: songInformation.durationInSec * 1000, + userInput: query, + }; } module.exports.getStream = getStream; \ No newline at end of file diff --git a/music_sources/spotify.js b/music_sources/spotify.js index 5a7871a..1ee94ed 100644 --- a/music_sources/spotify.js +++ b/music_sources/spotify.js @@ -1,14 +1,14 @@ const playdl = require('play-dl'); -//TODO ADD SPOTIFY SUPPORT +// TODO ADD SPOTIFY SUPPORT async function getStream(query) { - const trackInfo = await playdl.spotify(query); + const trackInfo = await playdl.spotify(query); - let searched = await play.search(`${trackInfo.name}`, { - limit: 1 - }) + const searched = await play.search(`${trackInfo.name}`, { + limit: 1, + }); - let stream = await play.stream(searched[0].url) - return stream; + const stream = await play.stream(searched[0].url); + return stream; } module.exports.getStream = getStream; \ No newline at end of file diff --git a/music_sources/youtube.js b/music_sources/youtube.js index d92b38a..ff7ac87 100644 --- a/music_sources/youtube.js +++ b/music_sources/youtube.js @@ -1,59 +1,60 @@ -const ytsr = require("ytsr"); -const playdl = require("play-dl"); +const ytsr = require('ytsr'); +const playdl = require('play-dl'); async function getStream(query) { - try { - const regex = /(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^?&\n]+)/; - const match = query.match(regex); - let videoId; - let usingYtsr = false; - if (match == null) { - let result = await playdl.search(query, { limit: 1 }); - videoId = result[0].id; + try { + const regex = /(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^?&\n]+)/; + const match = query.match(regex); + let videoId; + let usingYtsr = false; + if (match == null) { + const result = await playdl.search(query, { limit: 1 }); + videoId = result[0].id; - if (videoId == null) { - usingYtsr = true; - const searchResults = await ytsr(query, { - page: 1, - type: "video", - }); - videoId = searchResults.items[0].id; - } - } else { - videoId = match[1]; - } + if (videoId == null) { + usingYtsr = true; + const searchResults = await ytsr(query, { + page: 1, + type: 'video', + }); + videoId = searchResults.items[0].id; + } + } else { + // use array destructing to get the video id + [, videoId] = match; + } - const streamResult = await playdl.stream( - `https://www.youtube.com/watch?v=${videoId}`, - { quality: 2 } - ); - const infoResult = usingYtsr - ? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, { - limit: 1, - }) - : await playdl.video_info( - `https://www.youtube.com/watch?v=${videoId}` - ); - console.log("\x1b[36m"," Id: ", videoId, "Alternative search:", usingYtsr); - return { - title: - (usingYtsr - ? infoResult.items[0].title - : infoResult.video_details.title) ?? - 0, - duration: - (usingYtsr - ? infoResult.items[0].duration - : (infoResult.video_details.durationInSec * 1000)) ?? - 0, - stream: streamResult.stream, - type: streamResult.type, - userInput: query, - }; - } catch (error) { - console.log("\x1b[31m", error); - return null; - } + const streamResult = await playdl.stream( + `https://www.youtube.com/watch?v=${videoId}`, + { quality: 2 }, + ); + const infoResult = usingYtsr + ? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, { + limit: 1, + }) + : await playdl.video_info( + `https://www.youtube.com/watch?v=${videoId}`, + ); + console.log('\x1b[36m', ' Id: ', videoId, 'Alternative search:', usingYtsr); + return { + title: + (usingYtsr + ? infoResult.items[0].title + : infoResult.video_details.title) + ?? 0, + duration: + (usingYtsr + ? infoResult.items[0].duration + : (infoResult.video_details.durationInSec * 1000)) + ?? 0, + stream: streamResult.stream, + type: streamResult.type, + userInput: query, + }; + } catch (error) { + console.log('\x1b[31m', error); + return null; + } } -module.exports.getStream = getStream; +module.exports.getStream = getStream; \ No newline at end of file diff --git a/package.json b/package.json index e79ab75..35f7bde 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "node index.js" + "start": "node index.js", + "lint": "eslint --fix --ext .js,.jsx ." }, "repository": { "type": "git", @@ -22,7 +23,9 @@ "dependencies": { "@discordjs/builders": "^1.6.3", "@discordjs/opus": "^0.9.0", + "@discordjs/rest": "^1.7.1", "@discordjs/voice": "^0.16.0", + "discord-api-types": "^0.37.46", "discord.js": "^14.11.0", "dotenv": "^16.3.1", "ffmpeg-static": "^4.2.7", @@ -33,6 +36,11 @@ "tweetnacl": "^1.0.3", "ytdl-core": "^4.11.4", "ytsr": "^3.8.2" + }, + "devDependencies": { + "eslint": "^8.43.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.27.5", + "prettier": "^2.8.8" } } - diff --git a/reAuthenticate.js b/reAuthenticate.js index 0d3148f..06ccab7 100644 --- a/reAuthenticate.js +++ b/reAuthenticate.js @@ -1,18 +1,17 @@ const play = require('play-dl'); async function ReAuth() { + play.getFreeClientID().then((clientID) => { + play.setToken({ + soundcloud: { + client_id: clientID, + }, + }); -play.getFreeClientID().then((clientID) => { - play.setToken({ - soundcloud : { - client_id : clientID - } - }) + console.log(`Soudncloud Client ID: ${clientID}`); + }); - console.log('Soudncloud Client ID: ' + clientID); -}) - -play.authorization(); + play.authorization(); } module.exports.ReAuth = ReAuth; diff --git a/utils/getMusicStream.js b/utils/getMusicStream.js index 73a041d..83da29d 100644 --- a/utils/getMusicStream.js +++ b/utils/getMusicStream.js @@ -1,44 +1,41 @@ +const { StreamType } = require('@discordjs/voice'); const spotify = require('../music_sources/spotify'); const soundcloud = require('../music_sources/soundcloud'); const youtube = require('../music_sources/youtube'); -const { StreamType } = require('@discordjs/voice'); - async function getMusicStream(query) { - let stream; - let songTitle; - let songDuration; - let type = StreamType.Opus; - let userInput = query; + let stream; + let songTitle; + let songDuration; + let type = StreamType.Opus; + const userInput = query; - if (query.includes('spotify.com')) { - stream = await spotify.getStream(query); - songTitle = stream.title; - songDuration = stream.duration; - stream = stream.stream; + if (query.includes('spotify.com')) { + stream = await spotify.getStream(query); + songTitle = stream.title; + songDuration = stream.duration; + stream = stream.stream; + } else if (query.includes('soundcloud.com')) { + stream = await soundcloud.getStream(query); + songTitle = stream.title; + songDuration = stream.duration; + stream = stream.stream; + type = StreamType.OggOpus; + } else { + stream = await youtube.getStream(query); + songTitle = stream?.title ?? 'Unknown'; + songDuration = stream?.duration ?? 'Unknown'; + stream = stream?.stream; + type = StreamType.Opus; + } - } else if (query.includes('soundcloud.com')) { - stream = await soundcloud.getStream(query); - songTitle = stream.title; - songDuration = stream.duration; - stream = stream.stream; - type = StreamType.OggOpus; - - } else { - stream = await youtube.getStream(query) - songTitle = stream?.title ?? 'Unknown'; - songDuration = stream?.duration ?? 'Unknown'; - stream = stream?.stream; - type = StreamType.Opus; - } - - return { - title: songTitle, - duration: songDuration, - userInput: userInput, - stream: stream, - type: type - }; + return { + title: songTitle, + duration: songDuration, + userInput, + stream, + type, + }; } -module.exports.getMusicStream = getMusicStream; +module.exports.getMusicStream = getMusicStream; \ No newline at end of file diff --git a/utils/musicPlayer.js b/utils/musicPlayer.js index fd9d3ec..a328f51 100644 --- a/utils/musicPlayer.js +++ b/utils/musicPlayer.js @@ -1,74 +1,143 @@ +/* eslint-disable max-len */ +/* eslint-disable no-restricted-syntax */ const { - createAudioResource, - createAudioPlayer, - NoSubscriberBehavior, - AudioPlayerStatus, + createAudioResource, + createAudioPlayer, + NoSubscriberBehavior, + AudioPlayerStatus, } = require('@discordjs/voice'); +const process = require('dotenv').config(); + +const { clientId } = process.parsed; const { EmbedBuilder } = require('discord.js'); const musicQueue = require('../musicQueue'); -const { progressBar } = require('../utils/progress'); +const { progressBar } = require('./progress'); + +const currentInteractionIds = new Map(); +const currentInteractions = new Map(); +const oldConnections = []; +const timeoutTimer = new Map(); + +// TODO FIX THIS SHIT!!! ISSUES WITH DISPLAYING NAME AND STATUS WHEN UPDATING +function nowPLayingMessage(interaction, song, oldInteractionId) { + progressBar(0, 0, true); + + if (interaction.commandName === 'play') { + interaction.followUp('~🎵~').then((message) => { + const songTitle = song.title; + // const embed = new EmbedBuilder() + // .setColor('#E0B0FF') + // .setTitle(`Now playing: ${songTitle}`) + // .setDescription( + // progressBar(song.duration, 10).progressBarString, + // ); + const embed = new EmbedBuilder() + .setColor('#E0B0FF') + .setTitle(`Now playing: ${songTitle}`); + + message.edit({ + embeds: [embed], + }); + + const inter = setInterval(async () => { + const { progressBarString, isDone } = progressBar( + song.duration, + 10, + ); + if (isDone || message.id !== oldInteractionId) { + // clearInterval(inter); + return; + } + + if (message.id != null && interaction.guild.id !== oldInteractionId) { + interaction.channel.messages.fetch().then(async (channel) => { + const filter = channel.filter((msg) => msg.author.id === clientId); + const latestMessage = await interaction.channel.messages.fetch(filter.first().id); + latestMessage.edit({ + embeds: [embed.setTitle(`Now playing: ${songTitle}`)], + }); + }); + } + }, 2000); + + currentInteractionIds.set(interaction.guild.id, interaction); + currentInteractions.set(interaction.guild.id, interaction.id); + }); + } +} async function musicPlayer(guildId, connection, interaction) { - const serverQueue = musicQueue.getQueue(guildId); + try { + const oldInteractions = await currentInteractions.get(interaction.guild.id); + const oldInteractionId = await currentInteractionIds.get(interaction.guild.id); + const serverQueue = musicQueue.getQueue(guildId); + const oldConnection = oldConnections + .find((guidConnection) => guidConnection[0] === interaction.guild.id); - if (serverQueue.length === 0) { - connection.destroy(); - return; - } + if (serverQueue.length === 0) { + oldConnection[1].destroy(); + } - const song = serverQueue[0]; + const song = serverQueue[0]; - if (song.stream == null) { - musicQueue.removeFromQueue(guildId); - musicPlayer(guildId, connection); - return; - } + if (song.stream === undefined) { + musicQueue.removeFromQueue(guildId); + musicPlayer(guildId, connection); + return; + } - let resource = createAudioResource(song.stream, { - inputType: song.type - }) + const resource = createAudioResource(song.stream, { + inputType: song.type, + }); - let player = createAudioPlayer({ - behaviors: { - noSubscriber: NoSubscriberBehavior.Play - } - }) + const player = createAudioPlayer({ + behaviors: { + noSubscriber: NoSubscriberBehavior.Play, + }, + }); - player.play(resource) + player.play(resource); - connection.subscribe(player) + connection.subscribe(player); - progressBar(0, 0, true) + nowPLayingMessage(interaction, song, oldInteractions); - if(interaction.commandName == "play") { - interaction.followUp(`~🎵~`).then(message => { - const embed = new EmbedBuilder() - .setColor("#E0B0FF") - .setTitle("Now playing: " + song.title) - .setDescription(progressBar(song.duration, 10).progressBarString); - - message.edit({ embeds: [embed] }); - - inter = setInterval(() => { - const { progressBarString, isDone } = progressBar(song.duration, 10); - if (isDone) { - clearInterval(inter); - message.delete(); - } - embed.setDescription(progressBarString); - message.edit({ embeds: [embed] }); - }, 2000) - }); - } + oldConnections.push([interaction.guild.id, connection]); - player.on(AudioPlayerStatus.Idle, async () => { - console.log('Song ended:', song.title); - await musicQueue.removeFromQueue(guildId) - musicPlayer(guildId, connection, interaction); - }); + player.on(AudioPlayerStatus.Idle, async () => { + console.log('Song ended:', song.title); + if (serverQueue.length !== 1) { + await musicQueue.removeFromQueue(guildId); + musicPlayer(guildId, connection, interaction); + } - return player; + // timeoutTimer.set(guildId, setTimeout(async () => { + // await musicQueue.removeFromQueue(guildId); + // connection.destroy(); + // }, 10000)); + }); + + player.on(AudioPlayerStatus.Playing, () => { + console.log('pausing timer'); + clearTimeout( + timeoutTimer.get(guildId), + ); + }); + + if (oldInteractionId) { + oldInteractionId.channel.messages.fetch().then(async (channel) => { + const { lastMessage } = oldInteractionId.channel; + const filter = channel.filter((message) => message.author.id === clientId && message.id !== lastMessage.id); + setTimeout(() => { + oldInteractionId.channel.bulkDelete(filter); + }, 1000); + }); + } + } catch (error) { + console.error(error); + interaction.followUp('There was an error playing the song.'); + } } module.exports.musicPlayer = musicPlayer; \ No newline at end of file diff --git a/utils/progress.js b/utils/progress.js index 67ba2ee..896f576 100644 --- a/utils/progress.js +++ b/utils/progress.js @@ -3,36 +3,35 @@ let current = 0; let percentage; const progressBar = (totalInMilliseconds, size, reset = false) => { - if (reset) { - startTime = Date.now(); - current = 0; - } + if (reset) { + startTime = Date.now(); + current = 0; + } - if (!startTime) { - startTime = Date.now(); - } + if (!startTime) { + startTime = Date.now(); + } - current = Date.now() - startTime; + current = Date.now() - startTime; - const totalInSeconds = totalInMilliseconds / 1000; - percentage = Math.min((current / 1000) / totalInSeconds, 1); - const progress = Math.round((size * percentage)); - const emptyProgress = size - progress; + const totalInSeconds = totalInMilliseconds / 1000; + percentage = Math.min((current / 1000) / totalInSeconds, 1); + const progress = Math.round((size * percentage)); + const emptyProgress = size - progress; - const progressText = '▇'.repeat(progress); - const emptyProgressText = '—'.repeat(emptyProgress); - const percentageText = Math.round(percentage * 100) + '%'; + const progressText = '▇'.repeat(progress); + const emptyProgressText = '—'.repeat(emptyProgress); - let elapsedTimeText = new Date(current).toISOString().slice(11, -5); - let totalTimeText = new Date(totalInMilliseconds).toISOString().slice(11, -5); + let elapsedTimeText = new Date(current).toISOString().slice(11, -5); + let totalTimeText = new Date(totalInMilliseconds).toISOString().slice(11, -5); - if (totalTimeText.startsWith('00:')) { - elapsedTimeText = elapsedTimeText.slice(3); - totalTimeText = totalTimeText.slice(3); - } + if (totalTimeText.startsWith('00:')) { + elapsedTimeText = elapsedTimeText.slice(3); + totalTimeText = totalTimeText.slice(3); + } - const progressBarString = elapsedTimeText + ' [' + progressText + emptyProgressText + ']' + percentageText + ' ' + totalTimeText; // Creating and returning the bar + const progressBarString = `${elapsedTimeText} \`\`\`${progressText}${emptyProgressText}\`\`\` ${totalTimeText}`; // Creating and returning the bar - return { progressBarString, isDone: percentage === 1 }; -} -module.exports.progressBar = progressBar; + return { progressBarString, isDone: percentage === 1 }; +}; +module.exports.progressBar = progressBar; \ No newline at end of file diff --git a/utils/registerCommands.js b/utils/registerCommands.js index c37327e..651594b 100644 --- a/utils/registerCommands.js +++ b/utils/registerCommands.js @@ -1,59 +1,55 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); -const { REST } = require("@discordjs/rest"); -const { Routes } = require("discord-api-types/v9"); +const { SlashCommandBuilder } = require('@discordjs/builders'); +const { REST } = require('@discordjs/rest'); +const { Routes } = require('discord-api-types/v9'); async function registerCommands(clientId, token) { - const commands = [ - new SlashCommandBuilder() - .setName("play") - .setDescription("Plays songs!") - .addStringOption((option) => - option - .setName("input") - .setDescription("Play song from YouTube, Spotify, SoundCloud, etc.") - .setRequired(true) - ), - new SlashCommandBuilder() - .setName("queue") - .setDescription("Adds a song to the queue!") - .addStringOption((option) => - option - .setName("song") - .setDescription( - "Add song from YouTube, Spotify, SoundCloud, etc. to the queue" - ) - .setRequired(true) - ), - new SlashCommandBuilder() - .setName("pause") - .setDescription("Pauses the current song!"), - new SlashCommandBuilder() - .setName("resume") - .setDescription("Resumes the current song!"), - new SlashCommandBuilder() - .setName("loop") - .setDescription("Loops the current song! (toggle)"), - new SlashCommandBuilder() - .setName("stop") - .setDescription("Stops the current song!"), - ]; + const commands = [ + new SlashCommandBuilder() + .setName('play') + .setDescription('Plays songs!') + .addStringOption((option) => option + .setName('input') + .setDescription('Play song from YouTube, Spotify, SoundCloud, etc.') + .setRequired(true)), + new SlashCommandBuilder() + .setName('queue') + .setDescription('Adds a song to the queue!') + .addStringOption((option) => option + .setName('song') + .setDescription( + 'Add song from YouTube, Spotify, SoundCloud, etc. to the queue', + ) + .setRequired(true)), + new SlashCommandBuilder() + .setName('pause') + .setDescription('Pauses the current song!'), + new SlashCommandBuilder() + .setName('resume') + .setDescription('Resumes the current song!'), + new SlashCommandBuilder() + .setName('loop') + .setDescription('Loops the current song! (toggle)'), + new SlashCommandBuilder() + .setName('stop') + .setDescription('Stops the current song!'), + ]; - const rest = new REST({ - version: "9", - }) - .setToken(token); + const rest = new REST({ + version: '9', + }) + .setToken(token); - try { - console.log("\x1b[35m", "Started refreshing application (/) commands."); + try { + console.log('\x1b[35m', 'Started refreshing application (/) commands.'); - await rest.put(Routes.applicationCommands(clientId), { - body: commands, - }); + await rest.put(Routes.applicationCommands(clientId), { + body: commands, + }); - console.log("\x1b[35m", "Successfully reloaded application (/) commands."); - } catch (error) { - console.error(error); - } + console.log('\x1b[35m', 'Successfully reloaded application (/) commands.'); + } catch (error) { + console.error(error); + } } module.exports.registerCommands = registerCommands; \ No newline at end of file