SamDizzle420

Discord Music Bot

Jul 15th, 2023
193
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const { Client, GatewayIntentBits, ButtonStyle, ButtonBuilder, ActionRowBuilder, EmbedBuilder } = require('discord.js');
  2. const { SlashCommandBuilder } = require('discord.js-slash-command');
  3. const { createAudioPlayer, createAudioResource, joinVoiceChannel, AudioPlayerStatus, NoSubscriberBehavior, StreamType } = require('@discordjs/voice');
  4. const SoundCloudDownloader = require('soundcloud-downloader');
  5. const SpotifyWebApi = require('spotify-web-api-node');
  6. require('dotenv').config();
  7.  
  8. const spotifyApi = new SpotifyWebApi({
  9.   clientId: process.env.SPOTIFY_CLIENT_ID,
  10.   clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
  11. });
  12.  
  13. async function getAccessToken() {
  14.   const data = await spotifyApi.clientCredentialsGrant();
  15.   return data.body.access_token;
  16. }
  17.  
  18. async function setAccessToken() {
  19.   const accessToken = await getAccessToken();
  20.   spotifyApi.setAccessToken(accessToken);
  21. }
  22.  
  23. const client = new Client({
  24.   intents: [
  25.     GatewayIntentBits.Guilds,
  26.     GatewayIntentBits.GuildVoiceStates
  27.   ]
  28. });
  29.  
  30. const queue = new Map();
  31. const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Pause } });
  32.  
  33. player.on(AudioPlayerStatus.Playing, () => {
  34.   console.log('Audio playback started.');
  35. });
  36.  
  37. client.on('ready', () => {
  38.   console.log(`Logged in as ${client.user.tag}`);
  39.   registerSlashCommands();
  40. });
  41.  
  42. client.on('interactionCreate', async (interaction) => {
  43.   if (!interaction.isCommand()) return;
  44.  
  45.   const { commandName, options } = interaction;
  46.  
  47.   if (commandName === 'play') {
  48.     const query = options.getString('query');
  49.     const voiceChannel = interaction.member.voice.channel;
  50.     if (!voiceChannel) {
  51.       return interaction.reply({ content: 'You need to be in a voice channel to use this command!', ephemeral: true });
  52.     }
  53.  
  54.     const connection = joinVoiceChannel({
  55.       channelId: voiceChannel.id,
  56.       guildId: voiceChannel.guild.id,
  57.       adapterCreator: voiceChannel.guild.voiceAdapterCreator,
  58.     });
  59.  
  60.     console.log('Voice connection:', connection);
  61.  
  62.     const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Pause } });
  63.    
  64.     if (commandName === 'play') {
  65.       const query = options.getString('query');
  66.     }
  67.  
  68.     try {
  69.       const trackInfo = await getTrackInfo(query);
  70.       console.log('Retrieved Track Info:', trackInfo);
  71.       const resource = await createAudioResource(trackInfo.url);
  72.       console.log('Created Audio Resource:', resource);
  73.       player.play(resource);
  74.       connection.subscribe(player);
  75.  
  76.       const serverQueue = getQueue(interaction.guildId);
  77.       serverQueue.songs.push(trackInfo);
  78.  
  79.       if (!serverQueue.playing) {
  80.         playSong(interaction.guildId);
  81.       }
  82.  
  83.       const embed = createNowPlayingEmbed(trackInfo);
  84.       const row = createPlaybackButtons();
  85.       interaction.reply({ embeds: [embed], components: [row] });
  86.     } catch (error) {
  87.       console.error(error);
  88.       interaction.reply({ content: 'An error occurred while playing the track.', ephemeral: true });
  89.     }
  90.   } else if (commandName === 'stop') {
  91.     stopPlayback(interaction.guildId);
  92.     interaction.reply('Stopped playback.');
  93.   } else if (commandName === 'queue') {
  94.     const serverQueue = getQueue(interaction.guildId);
  95.     if (serverQueue.songs.length === 0) {
  96.       interaction.reply('The queue is empty.');
  97.     } else {
  98.       const queueList = serverQueue.songs.map((song, index) => `${index + 1}. ${song.title}`);
  99.       interaction.reply(`Queue:\n${queueList.join('\n')}`);
  100.     }
  101.   }
  102. });
  103.  
  104. async function getTrackInfo(query) {
  105.   if (!query || query.trim().length === 0) {
  106.     throw new Error('Invalid query.');
  107.   }
  108.  
  109.   if (query.includes('soundcloud.com')) {
  110.     try {
  111.       const info = await SoundCloudDownloader.download(query);
  112.       return {
  113.         url: info.url,
  114.         title: info.title,
  115.         thumbnail: info.thumbnail,
  116.         duration: parseInt(info.duration),
  117.       };
  118.     } catch (error) {
  119.       throw new Error('Failed to fetch track information from SoundCloud.');
  120.     }
  121.   } else if (query.includes('spotify.com')) {
  122.     const match = query.match(/track\/(\w+)/);
  123.     if (match) {
  124.       try {
  125.         await setAccessToken();
  126.         const trackId = match[1];
  127.         const data = await spotifyApi.getTrack(trackId);
  128.         const track = data.body;
  129.         return {
  130.           url: track.external_urls.spotify,
  131.           title: `${track.name} - ${track.artists[0].name}`,
  132.           thumbnail: track.album.images[0].url,
  133.           duration: Math.floor(track.duration_ms / 1000),
  134.         };
  135.       } catch (error) {
  136.         throw new Error('Failed to fetch track information from Spotify.');
  137.       }
  138.     } else {
  139.       throw new Error('Invalid Spotify track URL.');
  140.     }
  141.   } else {
  142.     throw new Error('Unsupported URL or search query.');
  143.   }
  144. }
  145.  
  146. function getQueue(guildId) {
  147.   if (!queue.has(guildId)) {
  148.     queue.set(guildId, { songs: [], playing: false });
  149.   }
  150.   return queue.get(guildId);
  151. }
  152.  
  153. function playSong(guildId) {
  154.   const serverQueue = getQueue(guildId);
  155.  
  156.   if (serverQueue.songs.length === 0) {
  157.     serverQueue.playing = false;
  158.     return;
  159.   }
  160.  
  161.   const currentSong = serverQueue.songs[0];
  162.   const resource = createAudioResource(currentSong.url);
  163.   player.play(resource);
  164.  
  165.   player.on(AudioPlayerStatus.Idle, () => {
  166.     serverQueue.songs.shift();
  167.     playSong(guildId);
  168.  
  169.     const nowPlayingEmbed = createNowPlayingEmbed(serverQueue.songs[0]);
  170.     const nowPlayingMessage = client.channels.cache.get(serverQueue.textChannelId).messages.cache.get(serverQueue.nowPlayingMessageId);
  171.     nowPlayingMessage.edit({ embeds: [nowPlayingEmbed] });
  172.   });
  173.  
  174.   serverQueue.playing = true;
  175. }
  176.  
  177. function stopPlayback(guildId) {
  178.   const serverQueue = getQueue(guildId);
  179.   serverQueue.songs = [];
  180.   player.stop();
  181. }
  182.  
  183. function createNowPlayingEmbed(songInfo) {
  184.   const embed = new EmbedBuilder()
  185.     .setColor('#FF0000')
  186.     .setTitle('Now Playing')
  187.     .setDescription(songInfo.title)
  188.     .setThumbnail(songInfo.thumbnail)
  189.     .addFields(
  190.       { name: 'Duration', value: formatDuration(songInfo.duration) },
  191.       { name: 'Progress', value: '▶️' + createProgressBar(songInfo.duration, player.state.resource.playbackDuration) }
  192.     );
  193.  
  194.   return embed;
  195. }
  196. function createPlaybackButtons() {
  197.   const playButton = new ButtonBuilder()
  198.     .setCustomId('play')
  199.     .setLabel('▶️ Play')
  200.     .setStyle(ButtonStyle.Primary)
  201.  
  202.   const pauseButton = new ButtonBuilder()
  203.     .setCustomId('pause')
  204.     .setLabel('⏸️ Pause')
  205.     .setStyle(ButtonStyle.Primary)
  206.  
  207.   const nextButton = new ButtonBuilder()
  208.     .setCustomId('next')
  209.     .setLabel('⏭️ Next')
  210.     .setStyle(ButtonStyle.Primary)
  211.  
  212.   const previousButton = new ButtonBuilder()
  213.     .setCustomId('previous')
  214.     .setLabel('⏮️ Previous')
  215.     .setStyle(ButtonStyle.Primary)
  216.  
  217.   const row = new ActionRowBuilder().addComponents(playButton, pauseButton, previousButton, nextButton);
  218.  
  219.   return row;
  220. }
  221.  
  222. function formatDuration(duration) {
  223.   const minutes = Math.floor(duration / 60);
  224.   const seconds = duration % 60;
  225.   return `${minutes}:${seconds.toString().padStart(2, '0')}`;
  226. }
  227.  
  228. function createProgressBar(totalSeconds, currentSeconds) {
  229.     const length = 15;
  230.     const progress = Math.floor((currentSeconds / totalSeconds) * length);
  231.     const bar = '▬'.repeat(progress) + '🔘' + '▬'.repeat(length - progress);
  232.     return bar;
  233.   }
  234.  
  235.   function registerSlashCommands() {
  236.     client.guilds.cache.forEach(async (guild) => {
  237.       try {
  238.         await guild.commands.set([
  239.           {
  240.             name: 'play',
  241.             description: 'Play a song',
  242.             options: [
  243.               {
  244.                 name: 'query',
  245.                 description: 'Song name or URL',
  246.                 type: 3, // Update the type value to 3
  247.                 required: true,
  248.               },
  249.             ],
  250.           },
  251.           {
  252.             name: 'stop',
  253.             description: 'Stop playback',
  254.           },
  255.           {
  256.             name: 'queue',
  257.             description: 'Show the current song queue',
  258.           },
  259.         ]);
  260.         console.log(`Registered slash commands for guild ${guild.name}`);
  261.       } catch (error) {
  262.         console.error(`Failed to register slash commands for guild ${guild.name}:`, error);
  263.       }
  264.     });
  265.   }
  266.  
  267.   client.login(process.env.TOKEN);
Advertisement
Add Comment
Please, Sign In to add comment