Merge pull request #2 from Myxelium/pause-resume-loop

Halfbroken fixes
This commit was merged in pull request #2.
This commit is contained in:
2023-06-28 03:04:32 +02:00
committed by GitHub
18 changed files with 545 additions and 421 deletions

36
.eslintrc.js Normal file
View File

@@ -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'],
},
};

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"prettier.useTabs": true,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"eslint.validate": ["javascript"]
// "editor.formatOnSave": true,
// "eslint.format.enable": true
}

View File

@@ -1,34 +1,34 @@
const musicQueue = require("../musicQueue"); const musicQueue = require('../musicQueue');
async function enableLooping(interaction) { async function enableLooping(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const guildId = interaction.guild.id; const guildId = interaction.guild.id;
musicQueue.enableLooping(guildId); musicQueue.enableLooping(guildId);
interaction.followUp("Enabled looping for the current queue."); interaction.followUp('Enabled looping for the current queue.');
} }
async function unloopCommand(interaction) { async function unloopCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const guildId = interaction.guild.id; const guildId = interaction.guild.id;
musicQueue.disableLooping(guildId); musicQueue.disableLooping(guildId);
interaction.followUp("Disabled looping for the current queue."); interaction.followUp('Disabled looping for the current queue.');
} }
async function toggleLoopCommand(interaction) { async function toggleLoopCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const guildId = interaction.guild.id; const guildId = interaction.guild.id;
if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) { if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) {
musicQueue.disableLooping(guildId, false); musicQueue.disableLooping(guildId, false);
interaction.followUp("Disabled looping for the current queue."); interaction.followUp('Disabled looping for the current queue.');
} else { } else {
musicQueue.enableLooping(guildId, true); musicQueue.enableLooping(guildId, true);
interaction.followUp("Enabled looping for the current queue."); interaction.followUp('Enabled looping for the current queue.');
} }
} }
module.exports.toggleLoopCommand = toggleLoopCommand; module.exports.toggleLoopCommand = toggleLoopCommand;
module.exports.unloopCommand = unloopCommand; module.exports.unloopCommand = unloopCommand;
module.exports.enableLooping = enableLooping; module.exports.enableLooping = enableLooping;

View File

@@ -1,34 +1,34 @@
const { getVoiceConnection } = require("@discordjs/voice"); const { getVoiceConnection } = require('@discordjs/voice');
async function pauseCommand(interaction) { async function pauseCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const connection = getVoiceConnection(interaction.guild.id); const connection = getVoiceConnection(interaction.guild.id);
if (!connection) { if (!connection) {
return interaction.followUp( return interaction.followUp(
"There is no active music player in this server." 'There is no active music player in this server.',
); );
} }
connection.state.subscription.player.pause(); connection.state.subscription.player.pause();
interaction.followUp("Paused the music."); return interaction.followUp('Paused the music.');
} }
async function unpauseCommand(interaction) { async function unpauseCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const connection = getVoiceConnection(interaction.guild.id); const connection = getVoiceConnection(interaction.guild.id);
if (!connection) { if (!connection) {
return interaction.followUp( return interaction.followUp(
"There is no active music player in this server." 'There is no active music player in this server.',
); );
} }
connection.state.subscription.player.unpause(); connection.state.subscription.player.unpause();
interaction.followUp("Resumed the music."); return interaction.followUp('Resumed the music.');
} }
module.exports.pauseCommand = pauseCommand; module.exports.pauseCommand = pauseCommand;
module.exports.unpauseCommand = unpauseCommand; module.exports.unpauseCommand = unpauseCommand;

View File

@@ -1,32 +1,41 @@
const { getMusicStream } = require("./../utils/getMusicStream"); /* eslint-disable consistent-return */
const musicQueue = require("../musicQueue"); const { joinVoiceChannel } = require('@discordjs/voice');
const { musicPlayer } = require("../utils/musicPlayer"); const { getMusicStream } = require('../utils/getMusicStream');
const { joinVoiceChannel } = require("@discordjs/voice"); const musicQueue = require('../musicQueue');
const { musicPlayer } = require('../utils/musicPlayer');
async function playCommand(interaction) { async function playCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const query = interaction.options.getString("input"); const query = interaction.options.getString('input');
const voiceChannel = interaction.member.voice.channel; const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) { if (!voiceChannel) {
return interaction.followUp( return interaction.followUp(
"You must be in a voice channel to use this command." 'You must be in a voice channel to use this command.',
); );
} }
const song = await getMusicStream(query); 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({ if (musicQueue.getQueue(interaction.guild.id).length > 0) {
channelId: voiceChannel.id, musicQueue.removeFromQueue(interaction.guild.id);
guildId: interaction.guild.id, }
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false,
});
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;

View File

@@ -1,27 +1,27 @@
const musicQueue = require("../musicQueue"); const musicQueue = require('../musicQueue');
const { getMusicStream } = require("../utils/getMusicStream"); const { getMusicStream } = require('../utils/getMusicStream');
async function queueCommand(interaction) { async function queueCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const query = interaction.options.getString("song"); const query = interaction.options.getString('song');
const voiceChannel = interaction.member.voice.channel; const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) { if (!voiceChannel) {
return interaction.followUp( return interaction.followUp(
"You must be in a voice channel to use this command." 'You must be in a voice channel to use this command.',
); );
} }
const song = await getMusicStream(query); const song = await getMusicStream(query);
if (!song) { if (!song) {
return interaction return interaction
.followUp("Error finding song. Try Again.") .followUp('Error finding song. Try Again.')
.then((msg) => setTimeout(() => msg.delete(), 10000)); .then((msg) => setTimeout(() => msg.delete(), 10000));
} }
musicQueue.addToQueue(interaction.guild.id, song); musicQueue.addToQueue(interaction.guild.id, song);
interaction.followUp(`Added ${song.title} to the queue.`); return interaction.followUp(`Added ${song.title} to the queue.`);
} }
module.exports.queueCommand = queueCommand; module.exports.queueCommand = queueCommand;

View File

@@ -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) { async function stopCommand(interaction) {
await interaction.deferReply(); await interaction.deferReply();
const voiceChannel = interaction.member.voice.channel; const voiceChannel = interaction.member.voice.channel;
const connection = getVoiceConnection(interaction.guild.id); const connection = getVoiceConnection(interaction.guild.id);
if (!voiceChannel) { if (!voiceChannel) {
return interaction.followUp( return interaction.followUp(
"You must be in a voice channel to use this command." '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) { if (!connection.state.subscription.player) {
return interaction.followUp( return interaction.followUp(
"I am not currently playing music in a voice channel." 'I am not currently playing music in a voice channel.',
); );
} }
connection.state.subscription.player.stop(); connection.state.subscription.player.stop();
musicQueue.clearQueue(guildId); 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;

View File

@@ -1,4 +1,5 @@
const { Client, GatewayIntentBits } = require('discord.js'); const { Client, GatewayIntentBits } = require('discord.js');
const process = require('dotenv').config();
const { registerCommands } = require('./utils/registerCommands'); const { registerCommands } = require('./utils/registerCommands');
const { playCommand } = require('./commands/play'); const { playCommand } = require('./commands/play');
const { queueCommand } = require('./commands/queue'); const { queueCommand } = require('./commands/queue');
@@ -7,50 +8,49 @@ const { pauseCommand, unpauseCommand } = require('./commands/pauseResume');
const { toggleLoopCommand } = require('./commands/loop'); const { toggleLoopCommand } = require('./commands/loop');
const { ReAuth } = require('./reAuthenticate'); const { ReAuth } = require('./reAuthenticate');
const process = require('dotenv').config(); const { clientId } = process.parsed;
const clientId = process.parsed.clientId; const { token } = process.parsed;
const token = process.parsed.token;
const client = new Client({ const client = new Client({
intents: [ intents: [
GatewayIntentBits.Guilds, GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent, GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildIntegrations GatewayIntentBits.GuildIntegrations,
] ],
}) });
client.on('ready', async () => { client.on('ready', async () => {
console.log(`Logged in as ${client.user.tag}!`); console.log(`Logged in as ${client.user.tag}!`);
await registerCommands(clientId, token); await registerCommands(clientId, token);
}); });
client.on('interactionCreate', async (interaction) => { client.on('interactionCreate', async (interaction) => {
if (!interaction.isCommand()) return; if (!interaction.isCommand()) return;
const { commandName } = interaction; const { commandName } = interaction;
if (commandName === 'play') { if (commandName === 'play') {
await playCommand(interaction); await playCommand(interaction, client);
} else if (commandName === 'queue') { } else if (commandName === 'queue') {
await queueCommand(interaction); await queueCommand(interaction);
} else if (commandName === 'pause') { } else if (commandName === 'pause') {
await pauseCommand(interaction); await pauseCommand(interaction);
} else if (commandName === 'resume') { } else if (commandName === 'resume') {
await unpauseCommand(interaction); await unpauseCommand(interaction);
} else if (commandName === 'loop') { } else if (commandName === 'loop') {
await toggleLoopCommand(interaction); await toggleLoopCommand(interaction);
} else if (commandName === 'stop') { } else if (commandName === 'stop') {
await stopCommand(interaction); await stopCommand(interaction);
} }
}); });
client.on('messageCreate', async (message) => { client.on('messageCreate', async (message) => {
if(message.content == 'reauth') { if (message.content === 'reauth') {
await ReAuth(); await ReAuth();
} }
}); });
client.login(token); client.login(token);

View File

@@ -1,58 +1,58 @@
const { getMusicStream } = require('./utils/getMusicStream'); const { getMusicStream } = require('./utils/getMusicStream');
class MusicQueue { class MusicQueue {
constructor() { constructor() {
this.queue = new Map(); this.queue = new Map();
this.looping = new Map(); this.looping = new Map();
} }
addToQueue(guildId, song) { addToQueue(guildId, song) {
if (!this.queue.has(guildId)) { if (!this.queue.has(guildId)) {
this.queue.set(guildId, []); this.queue.set(guildId, []);
} }
this.queue.get(guildId).push(song); this.queue.get(guildId).push(song);
} }
async removeFromQueue(guildId) { async removeFromQueue(guildId) {
if (!this.queue.has(guildId)) { if (!this.queue.has(guildId)) {
return; return;
} }
const serverQueue = this.queue.get(guildId); const serverQueue = this.queue.get(guildId);
if (this.looping.has(guildId) && this.looping.get(guildId)) { if (this.looping.has(guildId) && this.looping.get(guildId)) {
const song = serverQueue.shift(); const song = serverQueue.shift();
const newSong = await getMusicStream(song.userInput); const newSong = await getMusicStream(song.userInput);
serverQueue.push(newSong); serverQueue.push(newSong);
} else { } else {
serverQueue.shift(); serverQueue.shift();
} }
} }
getQueue(guildId) { getQueue(guildId) {
if (!this.queue.has(guildId)) { if (!this.queue.has(guildId)) {
return []; return [];
} }
return this.queue.get(guildId); return this.queue.get(guildId);
} }
enableLooping(guildId) { enableLooping(guildId) {
this.looping.set(guildId, true); this.looping.set(guildId, true);
} }
disableLooping(guildId) { disableLooping(guildId) {
this.looping.set(guildId, false); this.looping.set(guildId, false);
} }
clearQueue(guildId) { clearQueue(guildId) {
if (!this.queue.has(guildId)) { if (!this.queue.has(guildId)) {
return; return;
} }
this.queue.set(guildId, []); this.queue.set(guildId, []);
} }
} }
module.exports = new MusicQueue(); module.exports = new MusicQueue();

View File

@@ -1,14 +1,14 @@
const playdl = require("play-dl"); const playdl = require('play-dl');
async function getStream(query) { async function getStream(query) {
let songInformation = await playdl.soundcloud(query); const songInformation = await playdl.soundcloud(query);
let stream = await playdl.stream_from_info(songInformation, { quality: 2 }); const stream = await playdl.stream_from_info(songInformation, { quality: 2 });
return { return {
title: songInformation.name, title: songInformation.name,
stream: stream.stream, stream: stream.stream,
duration: songInformation.durationInSec * 1000, duration: songInformation.durationInSec * 1000,
userInput: query, userInput: query,
}; };
} }
module.exports.getStream = getStream; module.exports.getStream = getStream;

View File

@@ -1,14 +1,14 @@
const playdl = require('play-dl'); const playdl = require('play-dl');
//TODO ADD SPOTIFY SUPPORT // TODO ADD SPOTIFY SUPPORT
async function getStream(query) { async function getStream(query) {
const trackInfo = await playdl.spotify(query); const trackInfo = await playdl.spotify(query);
let searched = await play.search(`${trackInfo.name}`, { const searched = await play.search(`${trackInfo.name}`, {
limit: 1 limit: 1,
}) });
let stream = await play.stream(searched[0].url) const stream = await play.stream(searched[0].url);
return stream; return stream;
} }
module.exports.getStream = getStream; module.exports.getStream = getStream;

View File

@@ -1,59 +1,60 @@
const ytsr = require("ytsr"); const ytsr = require('ytsr');
const playdl = require("play-dl"); const playdl = require('play-dl');
async function getStream(query) { async function getStream(query) {
try { try {
const regex = /(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^?&\n]+)/; const regex = /(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^?&\n]+)/;
const match = query.match(regex); const match = query.match(regex);
let videoId; let videoId;
let usingYtsr = false; let usingYtsr = false;
if (match == null) { if (match == null) {
let result = await playdl.search(query, { limit: 1 }); const result = await playdl.search(query, { limit: 1 });
videoId = result[0].id; videoId = result[0].id;
if (videoId == null) { if (videoId == null) {
usingYtsr = true; usingYtsr = true;
const searchResults = await ytsr(query, { const searchResults = await ytsr(query, {
page: 1, page: 1,
type: "video", type: 'video',
}); });
videoId = searchResults.items[0].id; videoId = searchResults.items[0].id;
} }
} else { } else {
videoId = match[1]; // use array destructing to get the video id
} [, videoId] = match;
}
const streamResult = await playdl.stream( const streamResult = await playdl.stream(
`https://www.youtube.com/watch?v=${videoId}`, `https://www.youtube.com/watch?v=${videoId}`,
{ quality: 2 } { quality: 2 },
); );
const infoResult = usingYtsr const infoResult = usingYtsr
? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, { ? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, {
limit: 1, limit: 1,
}) })
: await playdl.video_info( : await playdl.video_info(
`https://www.youtube.com/watch?v=${videoId}` `https://www.youtube.com/watch?v=${videoId}`,
); );
console.log("\x1b[36m"," Id: ", videoId, "Alternative search:", usingYtsr); console.log('\x1b[36m', ' Id: ', videoId, 'Alternative search:', usingYtsr);
return { return {
title: title:
(usingYtsr (usingYtsr
? infoResult.items[0].title ? infoResult.items[0].title
: infoResult.video_details.title) ?? : infoResult.video_details.title)
0, ?? 0,
duration: duration:
(usingYtsr (usingYtsr
? infoResult.items[0].duration ? infoResult.items[0].duration
: (infoResult.video_details.durationInSec * 1000)) ?? : (infoResult.video_details.durationInSec * 1000))
0, ?? 0,
stream: streamResult.stream, stream: streamResult.stream,
type: streamResult.type, type: streamResult.type,
userInput: query, userInput: query,
}; };
} catch (error) { } catch (error) {
console.log("\x1b[31m", error); console.log('\x1b[31m', error);
return null; return null;
} }
} }
module.exports.getStream = getStream; module.exports.getStream = getStream;

View File

@@ -5,7 +5,8 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js" "start": "node index.js",
"lint": "eslint --fix --ext .js,.jsx ."
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -22,7 +23,9 @@
"dependencies": { "dependencies": {
"@discordjs/builders": "^1.6.3", "@discordjs/builders": "^1.6.3",
"@discordjs/opus": "^0.9.0", "@discordjs/opus": "^0.9.0",
"@discordjs/rest": "^1.7.1",
"@discordjs/voice": "^0.16.0", "@discordjs/voice": "^0.16.0",
"discord-api-types": "^0.37.46",
"discord.js": "^14.11.0", "discord.js": "^14.11.0",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"ffmpeg-static": "^4.2.7", "ffmpeg-static": "^4.2.7",
@@ -33,6 +36,11 @@
"tweetnacl": "^1.0.3", "tweetnacl": "^1.0.3",
"ytdl-core": "^4.11.4", "ytdl-core": "^4.11.4",
"ytsr": "^3.8.2" "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"
} }
} }

View File

@@ -1,18 +1,17 @@
const play = require('play-dl'); const play = require('play-dl');
async function ReAuth() { async function ReAuth() {
play.getFreeClientID().then((clientID) => {
play.setToken({
soundcloud: {
client_id: clientID,
},
});
play.getFreeClientID().then((clientID) => { console.log(`Soudncloud Client ID: ${clientID}`);
play.setToken({ });
soundcloud : {
client_id : clientID
}
})
console.log('Soudncloud Client ID: ' + clientID); play.authorization();
})
play.authorization();
} }
module.exports.ReAuth = ReAuth; module.exports.ReAuth = ReAuth;

View File

@@ -1,44 +1,41 @@
const { StreamType } = require('@discordjs/voice');
const spotify = require('../music_sources/spotify'); const spotify = require('../music_sources/spotify');
const soundcloud = require('../music_sources/soundcloud'); const soundcloud = require('../music_sources/soundcloud');
const youtube = require('../music_sources/youtube'); const youtube = require('../music_sources/youtube');
const { StreamType } = require('@discordjs/voice');
async function getMusicStream(query) { async function getMusicStream(query) {
let stream; let stream;
let songTitle; let songTitle;
let songDuration; let songDuration;
let type = StreamType.Opus; let type = StreamType.Opus;
let userInput = query; const userInput = query;
if (query.includes('spotify.com')) { if (query.includes('spotify.com')) {
stream = await spotify.getStream(query); stream = await spotify.getStream(query);
songTitle = stream.title; songTitle = stream.title;
songDuration = stream.duration; songDuration = stream.duration;
stream = stream.stream; 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')) { return {
stream = await soundcloud.getStream(query); title: songTitle,
songTitle = stream.title; duration: songDuration,
songDuration = stream.duration; userInput,
stream = stream.stream; stream,
type = StreamType.OggOpus; type,
};
} 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
};
} }
module.exports.getMusicStream = getMusicStream; module.exports.getMusicStream = getMusicStream;

View File

@@ -1,74 +1,143 @@
/* eslint-disable max-len */
/* eslint-disable no-restricted-syntax */
const { const {
createAudioResource, createAudioResource,
createAudioPlayer, createAudioPlayer,
NoSubscriberBehavior, NoSubscriberBehavior,
AudioPlayerStatus, AudioPlayerStatus,
} = require('@discordjs/voice'); } = require('@discordjs/voice');
const process = require('dotenv').config();
const { clientId } = process.parsed;
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const musicQueue = require('../musicQueue'); 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) { 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) { if (serverQueue.length === 0) {
connection.destroy(); oldConnection[1].destroy();
return; }
}
const song = serverQueue[0]; const song = serverQueue[0];
if (song.stream == null) { if (song.stream === undefined) {
musicQueue.removeFromQueue(guildId); musicQueue.removeFromQueue(guildId);
musicPlayer(guildId, connection); musicPlayer(guildId, connection);
return; return;
} }
let resource = createAudioResource(song.stream, { const resource = createAudioResource(song.stream, {
inputType: song.type inputType: song.type,
}) });
let player = createAudioPlayer({ const player = createAudioPlayer({
behaviors: { behaviors: {
noSubscriber: NoSubscriberBehavior.Play 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") { oldConnections.push([interaction.guild.id, connection]);
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)
});
}
player.on(AudioPlayerStatus.Idle, async () => { player.on(AudioPlayerStatus.Idle, async () => {
console.log('Song ended:', song.title); console.log('Song ended:', song.title);
await musicQueue.removeFromQueue(guildId) if (serverQueue.length !== 1) {
musicPlayer(guildId, connection, interaction); 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; module.exports.musicPlayer = musicPlayer;

View File

@@ -3,36 +3,35 @@ let current = 0;
let percentage; let percentage;
const progressBar = (totalInMilliseconds, size, reset = false) => { const progressBar = (totalInMilliseconds, size, reset = false) => {
if (reset) { if (reset) {
startTime = Date.now(); startTime = Date.now();
current = 0; current = 0;
} }
if (!startTime) { if (!startTime) {
startTime = Date.now(); startTime = Date.now();
} }
current = Date.now() - startTime; current = Date.now() - startTime;
const totalInSeconds = totalInMilliseconds / 1000; const totalInSeconds = totalInMilliseconds / 1000;
percentage = Math.min((current / 1000) / totalInSeconds, 1); percentage = Math.min((current / 1000) / totalInSeconds, 1);
const progress = Math.round((size * percentage)); const progress = Math.round((size * percentage));
const emptyProgress = size - progress; const emptyProgress = size - progress;
const progressText = '▇'.repeat(progress); const progressText = '▇'.repeat(progress);
const emptyProgressText = '—'.repeat(emptyProgress); const emptyProgressText = '—'.repeat(emptyProgress);
const percentageText = Math.round(percentage * 100) + '%';
let elapsedTimeText = new Date(current).toISOString().slice(11, -5); let elapsedTimeText = new Date(current).toISOString().slice(11, -5);
let totalTimeText = new Date(totalInMilliseconds).toISOString().slice(11, -5); let totalTimeText = new Date(totalInMilliseconds).toISOString().slice(11, -5);
if (totalTimeText.startsWith('00:')) { if (totalTimeText.startsWith('00:')) {
elapsedTimeText = elapsedTimeText.slice(3); elapsedTimeText = elapsedTimeText.slice(3);
totalTimeText = totalTimeText.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 }; return { progressBarString, isDone: percentage === 1 };
} };
module.exports.progressBar = progressBar; module.exports.progressBar = progressBar;

View File

@@ -1,59 +1,55 @@
const { SlashCommandBuilder } = require("@discordjs/builders"); const { SlashCommandBuilder } = require('@discordjs/builders');
const { REST } = require("@discordjs/rest"); const { REST } = require('@discordjs/rest');
const { Routes } = require("discord-api-types/v9"); const { Routes } = require('discord-api-types/v9');
async function registerCommands(clientId, token) { async function registerCommands(clientId, token) {
const commands = [ const commands = [
new SlashCommandBuilder() new SlashCommandBuilder()
.setName("play") .setName('play')
.setDescription("Plays songs!") .setDescription('Plays songs!')
.addStringOption((option) => .addStringOption((option) => option
option .setName('input')
.setName("input") .setDescription('Play song from YouTube, Spotify, SoundCloud, etc.')
.setDescription("Play song from YouTube, Spotify, SoundCloud, etc.") .setRequired(true)),
.setRequired(true) new SlashCommandBuilder()
), .setName('queue')
new SlashCommandBuilder() .setDescription('Adds a song to the queue!')
.setName("queue") .addStringOption((option) => option
.setDescription("Adds a song to the queue!") .setName('song')
.addStringOption((option) => .setDescription(
option 'Add song from YouTube, Spotify, SoundCloud, etc. to the queue',
.setName("song") )
.setDescription( .setRequired(true)),
"Add song from YouTube, Spotify, SoundCloud, etc. to the queue" new SlashCommandBuilder()
) .setName('pause')
.setRequired(true) .setDescription('Pauses the current song!'),
), new SlashCommandBuilder()
new SlashCommandBuilder() .setName('resume')
.setName("pause") .setDescription('Resumes the current song!'),
.setDescription("Pauses the current song!"), new SlashCommandBuilder()
new SlashCommandBuilder() .setName('loop')
.setName("resume") .setDescription('Loops the current song! (toggle)'),
.setDescription("Resumes the current song!"), new SlashCommandBuilder()
new SlashCommandBuilder() .setName('stop')
.setName("loop") .setDescription('Stops the current song!'),
.setDescription("Loops the current song! (toggle)"), ];
new SlashCommandBuilder()
.setName("stop")
.setDescription("Stops the current song!"),
];
const rest = new REST({ const rest = new REST({
version: "9", version: '9',
}) })
.setToken(token); .setToken(token);
try { try {
console.log("\x1b[35m", "Started refreshing application (/) commands."); console.log('\x1b[35m', 'Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(clientId), { await rest.put(Routes.applicationCommands(clientId), {
body: commands, body: commands,
}); });
console.log("\x1b[35m", "Successfully reloaded application (/) commands."); console.log('\x1b[35m', 'Successfully reloaded application (/) commands.');
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} }
module.exports.registerCommands = registerCommands; module.exports.registerCommands = registerCommands;