mirror of
				https://github.com/aydenjahola/discord-multipurpose-bot.git
				synced 2025-11-04 00:01:41 +00:00 
			
		
		
		
	add simple music functionality
This commit is contained in:
		
							parent
							
								
									51dcdc7406
								
							
						
					
					
						commit
						f0a936690b
					
				
					 7 changed files with 140 additions and 36 deletions
				
			
		
							
								
								
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,5 +1,16 @@
 | 
			
		|||
FROM jrottenberg/ffmpeg:8-alpine AS ffmpeg
 | 
			
		||||
 | 
			
		||||
FROM node:current-alpine
 | 
			
		||||
 | 
			
		||||
COPY --from=ffmpeg /usr/local/bin/ffmpeg /usr/local/bin/
 | 
			
		||||
COPY --from=ffmpeg /usr/local/bin/ffprobe /usr/local/bin/
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
    python3 \
 | 
			
		||||
    make \
 | 
			
		||||
    g++ \
 | 
			
		||||
    && npm install -g npm@latest
 | 
			
		||||
 | 
			
		||||
WORKDIR /usr/src/app
 | 
			
		||||
 | 
			
		||||
COPY package*.json ./
 | 
			
		||||
| 
						 | 
				
			
			@ -8,4 +19,4 @@ RUN npm install
 | 
			
		|||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
CMD ["node", "."]
 | 
			
		||||
CMD ["node", "."]
 | 
			
		||||
							
								
								
									
										31
									
								
								commands/music/play.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								commands/music/play.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
const { SlashCommandBuilder } = require("discord.js");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  data: new SlashCommandBuilder()
 | 
			
		||||
    .setName("play")
 | 
			
		||||
    .setDescription("Plays a song from YouTube, Spotify, or SoundCloud.")
 | 
			
		||||
    .addStringOption((option) =>
 | 
			
		||||
      option
 | 
			
		||||
        .setName("query")
 | 
			
		||||
        .setDescription("Song name or URL")
 | 
			
		||||
        .setRequired(true)
 | 
			
		||||
    ),
 | 
			
		||||
  async execute(interaction, client) {
 | 
			
		||||
    await interaction.deferReply();
 | 
			
		||||
    const query = interaction.options.getString("query");
 | 
			
		||||
    const voiceChannel = interaction.member.voice.channel;
 | 
			
		||||
    if (!voiceChannel) {
 | 
			
		||||
      return interaction.followUp("❌ You need to be in a voice channel!");
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
      await client.distube.play(voiceChannel, query, {
 | 
			
		||||
        textChannel: interaction.channel,
 | 
			
		||||
        member: interaction.member,
 | 
			
		||||
      });
 | 
			
		||||
      await interaction.followUp(`🔍 Searching: \`${query}\``);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error(error);
 | 
			
		||||
      interaction.followUp("❌ Failed to play the song.");
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										18
									
								
								commands/music/queue.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								commands/music/queue.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
const { SlashCommandBuilder } = require("discord.js");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  data: new SlashCommandBuilder()
 | 
			
		||||
    .setName("queue")
 | 
			
		||||
    .setDescription("Shows the current music queue."),
 | 
			
		||||
  async execute(interaction, client) {
 | 
			
		||||
    const queue = client.distube.getQueue(interaction.guildId);
 | 
			
		||||
    if (!queue || queue.songs.length === 0) {
 | 
			
		||||
      return interaction.reply("❌ The queue is empty!");
 | 
			
		||||
    }
 | 
			
		||||
    const tracks = queue.songs.map(
 | 
			
		||||
      (song, index) =>
 | 
			
		||||
        `${index + 1}. ${song.name} - \`${song.formattedDuration}\``
 | 
			
		||||
    );
 | 
			
		||||
    interaction.reply(`**Current Queue:**\n${tracks.join("\n")}`);
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								commands/music/skip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								commands/music/skip.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
const { SlashCommandBuilder } = require("discord.js");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  data: new SlashCommandBuilder()
 | 
			
		||||
    .setName("skip")
 | 
			
		||||
    .setDescription("Skips the current song."),
 | 
			
		||||
  async execute(interaction, client) {
 | 
			
		||||
    const queue = client.distube.getQueue(interaction.guildId);
 | 
			
		||||
    if (!queue) return interaction.reply("❌ No songs in queue!");
 | 
			
		||||
    try {
 | 
			
		||||
      await queue.skip();
 | 
			
		||||
      interaction.reply("⏭️ Skipped the current song!");
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      interaction.reply("❌ Failed to skip.");
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								commands/music/stop.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								commands/music/stop.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
const { SlashCommandBuilder } = require("discord.js");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  data: new SlashCommandBuilder()
 | 
			
		||||
    .setName("stop")
 | 
			
		||||
    .setDescription("Stops the music and clears the queue."),
 | 
			
		||||
  async execute(interaction, client) {
 | 
			
		||||
    const queue = client.distube.getQueue(interaction.guildId);
 | 
			
		||||
    if (!queue) return interaction.reply("❌ No music is playing!");
 | 
			
		||||
    try {
 | 
			
		||||
      await client.distube.stop(interaction.guildId);
 | 
			
		||||
      interaction.reply("🛑 Stopped the player and cleared the queue!");
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      interaction.reply("❌ Failed to stop.");
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										74
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								index.js
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -7,6 +7,9 @@ const {
 | 
			
		|||
  Routes,
 | 
			
		||||
  PresenceUpdateStatus,
 | 
			
		||||
} = require("discord.js");
 | 
			
		||||
const { DisTube } = require("distube");
 | 
			
		||||
const { SpotifyPlugin } = require("@distube/spotify");
 | 
			
		||||
const { SoundCloudPlugin } = require("@distube/soundcloud");
 | 
			
		||||
const mongoose = require("mongoose");
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const path = require("path");
 | 
			
		||||
| 
						 | 
				
			
			@ -19,41 +22,62 @@ const client = new Client({
 | 
			
		|||
    GatewayIntentBits.Guilds,
 | 
			
		||||
    GatewayIntentBits.GuildMessages,
 | 
			
		||||
    GatewayIntentBits.MessageContent,
 | 
			
		||||
    GatewayIntentBits.GuildVoiceStates,
 | 
			
		||||
  ],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
client.commands = new Collection();
 | 
			
		||||
 | 
			
		||||
// Initialize DisTube
 | 
			
		||||
client.distube = new DisTube(client, {
 | 
			
		||||
  emitNewSongOnly: true,
 | 
			
		||||
  plugins: [new SpotifyPlugin(), new SoundCloudPlugin()],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// DisTube event listeners
 | 
			
		||||
client.distube
 | 
			
		||||
  .on("playSong", (queue, song) => {
 | 
			
		||||
    queue.textChannel.send(
 | 
			
		||||
      `🎶 Playing: **${song.name}** - \`${song.formattedDuration}\``
 | 
			
		||||
    );
 | 
			
		||||
  })
 | 
			
		||||
  .on("addSong", (queue, song) => {
 | 
			
		||||
    queue.textChannel.send(
 | 
			
		||||
      `✅ Added: **${song.name}** - \`${song.formattedDuration}\``
 | 
			
		||||
    );
 | 
			
		||||
  })
 | 
			
		||||
  .on("error", (channel, error) => {
 | 
			
		||||
    console.error("DisTube error:", error);
 | 
			
		||||
    channel.send("❌ An error occurred: " + error.message);
 | 
			
		||||
  })
 | 
			
		||||
  .on("finish", (queue) => {
 | 
			
		||||
    queue.textChannel.send("🎵 Queue finished!");
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
// Function to recursively read commands from subdirectories
 | 
			
		||||
function loadCommands(dir) {
 | 
			
		||||
  const files = fs.readdirSync(dir);
 | 
			
		||||
 | 
			
		||||
  for (const file of files) {
 | 
			
		||||
    const filePath = path.join(dir, file);
 | 
			
		||||
 | 
			
		||||
    if (fs.statSync(filePath).isDirectory()) {
 | 
			
		||||
      // If it's a directory, recurse into it
 | 
			
		||||
      loadCommands(filePath);
 | 
			
		||||
    } else if (file.endsWith(".js")) {
 | 
			
		||||
      // If it's a JavaScript file, load the command
 | 
			
		||||
      const command = require(filePath);
 | 
			
		||||
      client.commands.set(command.data.name, command);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load all commands from the commands directory and its subdirectories
 | 
			
		||||
loadCommands(path.join(__dirname, "commands"));
 | 
			
		||||
 | 
			
		||||
async function registerCommands(guildId) {
 | 
			
		||||
  const commands = client.commands.map((cmd) => cmd.data.toJSON());
 | 
			
		||||
  const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN);
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    await rest.put(Routes.applicationGuildCommands(client.user.id, guildId), {
 | 
			
		||||
      body: commands,
 | 
			
		||||
    });
 | 
			
		||||
    console.log(`🔄 Successfully registered commands for guild: ${guildId}`);
 | 
			
		||||
    console.log(`🔄 Registered commands for guild: ${guildId}`);
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error("Error registering commands:", error);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -64,9 +88,7 @@ client.once("ready", async () => {
 | 
			
		|||
  console.log(`🤖 Logged in as ${client.user.tag}`);
 | 
			
		||||
  console.log(`==============================`);
 | 
			
		||||
 | 
			
		||||
  // Register commands for all existing guilds
 | 
			
		||||
  const guilds = client.guilds.cache.map((guild) => guild.id);
 | 
			
		||||
 | 
			
		||||
  await Promise.all(
 | 
			
		||||
    guilds.map(async (guildId) => {
 | 
			
		||||
      await seedShopItems(guildId);
 | 
			
		||||
| 
						 | 
				
			
			@ -75,29 +97,19 @@ client.once("ready", async () => {
 | 
			
		|||
    })
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Set bot status and activity
 | 
			
		||||
  client.user.setPresence({
 | 
			
		||||
    activities: [{ name: "Degenerate Gamers!", type: 3 }],
 | 
			
		||||
    status: PresenceUpdateStatus.Online,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  console.log(`\n==============================\n`);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Listen for new guild joins and register the guild ID in the database
 | 
			
		||||
client.on("guildCreate", async (guild) => {
 | 
			
		||||
  try {
 | 
			
		||||
    // Create a new entry in the ServerSettings collection with just the guildId
 | 
			
		||||
    await ServerSettings.create({ guildId: guild.id });
 | 
			
		||||
    console.log(`✅ Registered new server: ${guild.name} (ID: ${guild.id})`);
 | 
			
		||||
 | 
			
		||||
    // seed items for new guild with guildId
 | 
			
		||||
    await seedShopItems(guild.id);
 | 
			
		||||
 | 
			
		||||
    // Seed spyfall locations for the new guild
 | 
			
		||||
    await seedSpyfallLocations(guild.id);
 | 
			
		||||
 | 
			
		||||
    // Register slash commands for the new guild
 | 
			
		||||
    await registerCommands(guild.id);
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error("Error registering new server or commands:", error);
 | 
			
		||||
| 
						 | 
				
			
			@ -112,31 +124,23 @@ mongoose
 | 
			
		|||
 | 
			
		||||
client.on("interactionCreate", async (interaction) => {
 | 
			
		||||
  if (!interaction.isCommand()) return;
 | 
			
		||||
 | 
			
		||||
  const command = client.commands.get(interaction.commandName);
 | 
			
		||||
 | 
			
		||||
  if (!command) return;
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    await command.execute(interaction, client);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.error("Error executing command:", err);
 | 
			
		||||
    if (interaction.deferred || interaction.ephemeral) {
 | 
			
		||||
      await interaction.followUp({
 | 
			
		||||
        content: "There was an error while executing this command!",
 | 
			
		||||
        ephemeral: true,
 | 
			
		||||
      });
 | 
			
		||||
    const replyOptions = {
 | 
			
		||||
      content: "Error executing command!",
 | 
			
		||||
      ephemeral: true,
 | 
			
		||||
    };
 | 
			
		||||
    if (interaction.deferred || interaction.replied) {
 | 
			
		||||
      await interaction.followUp(replyOptions);
 | 
			
		||||
    } else {
 | 
			
		||||
      await interaction.reply({
 | 
			
		||||
        content: "There was an error while executing this command!",
 | 
			
		||||
        ephemeral: true,
 | 
			
		||||
      });
 | 
			
		||||
      await interaction.reply(replyOptions);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
client.on("error", (err) => {
 | 
			
		||||
  console.error("Client error:", err);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
client.on("error", (err) => console.error("Client error:", err));
 | 
			
		||||
client.login(process.env.BOT_TOKEN);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,8 +11,14 @@
 | 
			
		|||
  "keywords": [],
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@discordjs/opus": "^0.10.0",
 | 
			
		||||
    "@discordjs/voice": "^0.19.0",
 | 
			
		||||
    "@distube/soundcloud": "^2.0.4",
 | 
			
		||||
    "@distube/spotify": "^2.0.2",
 | 
			
		||||
    "@snazzah/davey": "^0.1.6",
 | 
			
		||||
    "axios": "^1.7.7",
 | 
			
		||||
    "discord.js": "^14.15.3",
 | 
			
		||||
    "distube": "^5.0.7",
 | 
			
		||||
    "dotenv": "^16.4.5",
 | 
			
		||||
    "express": "^4.19.2",
 | 
			
		||||
    "html-entities": "^2.5.2",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue