yukufy
v1.5.0
Published
A Player Package built with Node.js, using Spotify and SoundCloud as search and streaming engines, easy and simple to use for your bot.
Downloads
20
Maintainers
Readme
Yukufy
Yukufy
is a Node.js library designed for managing music playback in Discord servers. It allows you to search, play, pause, resume, skip tracks, stop playback, adjust volume, and fetch lyrics from Spotify and SoundCloud.
📃 Documentation
Explore the full documentation here.
Features
- Search and stream music from Spotify and SoundCloud.
- Manage playback with play, pause, resume, skip, and stop functionalities.
- Retrieve lyrics for the current or specified song.
- Adjust playback volume and manage a queue of songs.
⚙️ Having Issues? Join our Discord server for support and updates.
Note: This library is still under development and may contain bugs. Feel free to report issues or suggest features in our Discord!
> Yukufy Projects
- Yukufy Bot Example by lNazuna
- Yoruka by CroneGamesPlays
🛠️ Installation
Install Yukufy
via npm:
npm install yukufy
🛠️ Setup
To use Yukufy
, initialize the YukufyClient
with your Spotify credentials and player options.
const { Client, GatewayIntentBits } = require('discord.js');
const { YukufyClient } = require('yukufy');
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages] });
const yukufy = new YukufyClient(client, {
// Spotify API configuration
configApi: {
clientId: 'YOUR_SPOTIFY_CLIENT_ID',
clientSecret: 'YOUR_SPOTIFY_CLIENT_SECRET',
},
// Player configuration
configPlayer: {
defaultVolume: 75, // Default volume
leaveOnEmptyQueue: true,
leaveOnEmptyQueueCooldown: 10000
}
});
client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}`);
});
client.login('YOUR_DISCORD_BOT_TOKEN');
🎵 Usage
Player Functions
Search for Music
You can search for songs from Spotify or SoundCloud using the search
method.
const query = "NEFFEX Fight Back";
const source = "spotify"; // or "soundcloud"
const tracks = await yukufy.search(query, source);
console.log(tracks);
Play Music
To play a song, specify the song name and the voice channel.
const channel = interaction.member.voice.channel;
const song = "NEFFEX Michael Jordan";
const source = "spotify"; // or "soundcloud"
const music = await yukufy.play(song, channel, source, {
member: interaction.member,
textChannel: interaction.channel,
guildId: interaction.guild.id
});
Control the Player
Pause Music:
yukufy.pause();
Resume Music:
yukufy.resume();
Skip to Next Song:
yukufy.skip();
Stop Playback:
yukufy.stop();
Adjust Volume:
const volume = 50; // Volume level between 0 and 100
yukufy.setVolume(volume);
Get Lyrics
To fetch lyrics for the current song or a specific song:
const lyrics = await yukufy.lyrics("NEFFEX Fight Back");
console.log(lyrics);
Slash Commands
The bot supports various slash commands. Here’s how you can use them:
- /play [query]: Play a song in the voice channel. Example:
/play NEFFEX Fight Back
- /stop: Stop the music and leave the voice channel.
- /skip: Skip to the next song in the queue.
- /pause: Pause the current song.
- /resume: Resume the paused song.
- /volume [number]: Adjust the volume between 0 and 100. Example:
/volume 50
- /search [query] [source]: Search for a song on Spotify or SoundCloud. Example:
/search NEFFEX Fight Back spotify
- /queue: Show the song queue.
- /join: Join the voice channel.
- /leave: Leave the voice channel.
- /loop: Toggle the repeat mode.
- /nowplaying: Show the currently playing song.
- /lyrics [query]: Show the lyrics of the current song or a specific song. Example:
/lyrics NEFFEX Fight Back
Events
Yukufy also emits various events that you can listen to:
playSong
: Fired when a song starts playing.
yukufy.on('playSong', ({ track }) => {
console.log(`Now playing: ${track.title} by ${track.artist}`);
});
addSong
: Fired when a song is added to the queue.
yukufy.on('addSong', ({ track }) => {
console.log(`Song added to queue: ${track.title} by ${track.artist}`);
});
finishQueue
: Fired when the queue finishes.
yukufy.on('finishQueue', () => {
console.log('Music queue finished.');
});
emptyQueue
: Fired when the queue becomes empty.
yukufy.on('emptyQueue', () => {
console.log('Queue is empty.');
});
clientDisconnect
: Fired when the client disconnects from the voice channel.
yukufy.on('clientDisconnect', () => {
console.log('Disconnected from voice channel.');
});
playerError
: Fired when there is an error in the player.
yukufy.on('playerError', (error) => {
console.error('Player error:', error);
});
Yukufy Example Usage
const { Client, GatewayIntentBits, InteractionType, REST, Routes } = require('discord.js');
const { YukufyClient } = require('yukufy');
// Create a new Discord client instance with specified intents
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages
]
});
// Initialize the YukufyClient with configuration for API and player
const yukufy = new YukufyClient(client, {
configApi: {
clientId: "YourSpotifyClientId",
clientSecret: "YourSpotifyClientSecret"
},
configPlayer: {
defaultVolume: 75,
leaveOnEmptyQueue: true,
leaveOnEmptyQueueCooldown: 10000
}
});
const clientToken = "YourBotToken";
// Define the slash commands for the bot
const commands = [
{
name: 'play',
description: 'Play a song in the voice channel',
options: [
{
name: 'query',
type: 3, // Type STRING
description: 'Name of the song or URL',
required: true
}
]
},
{
name: 'stop',
description: 'Stop the music and leave the voice channel'
},
{
name: 'skip',
description: 'Skip to the next song in the queue'
},
{
name: 'pause',
description: 'Pause the current song'
},
{
name: 'resume',
description: 'Resume the paused song'
},
{
name: 'volume',
description: 'Adjust the volume of the player',
options: [
{
name: 'number',
type: 10, // Type NUMBER
description: 'Volume between 0 and 100',
required: true
}
]
},
{
name: 'search',
description: 'Search for a song on Spotify or SoundCloud',
options: [
{
name: 'query',
type: 3, // Type STRING
description: 'Name of the song or URL',
required: true
},
{
name: 'source',
type: 3, // Type STRING
description: 'Source (spotify or soundcloud)',
required: true,
choices: [
{
name: 'Spotify',
value: 'spotify'
},
{
name: 'SoundCloud',
value: 'soundcloud'
}
]
}
]
},
{
name: 'queue',
description: 'Show the song queue'
},
{
name: 'join',
description: 'Join the voice channel'
},
{
name: 'leave',
description: 'Leave the voice channel'
},
{
name: 'loop',
description: 'Toggle the repeat mode'
},
{
name: 'nowplaying',
description: 'Show the currently playing song'
},
{
name: 'lyrics',
description: 'Show the lyrics of the current song or a specific song',
options: [
{
name: 'query',
type: 3, // Type STRING
description: 'Name of the song (optional)',
required: false
}
]
}
];
// Initialize REST API with the bot token
const rest = new REST({ version: '10' }).setToken(clientToken);
async function main() {
try {
console.log('Starting slash command registration.');
// Register the slash commands with Discord
await rest.put(
Routes.applicationCommands(client.user.id),
{ body: commands }
);
console.log('Slash commands registered successfully.');
} catch (error) {
console.error('Error registering slash commands:', error);
}
// Event fired when the client is ready
client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}`);
});
// Event fired when an interaction is created
client.on('interactionCreate', async (interaction) => {
if (!interaction.isCommand()) return;
if (interaction.type !== InteractionType.ApplicationCommand) return;
if (!interaction.isChatInputCommand()) return;
const { commandName, options } = interaction;
// Handle the 'play' command
if (commandName === 'play') {
const song = options.getString('query');
const channel = interaction.member.voice.channel;
const source = "spotify"; // "soundcloud"
if (!channel) {
await interaction.reply({ content: 'You need to be in a voice channel to play music!' });
return;
}
try {
const music = await yukufy.play(song, channel, source, {
member: interaction.member,
textChannel: interaction.channel,
guildId: interaction.guild.id
});
if (!music) {
await interaction.reply('❌ Song not found.');
return;
}
const title = music.title || 'Title not available';
const artist = music.artist || 'Artist not available';
const duration = music.duration || 'Duration not available';
await interaction.reply({
content: `🔊 Searching... Song found: **${artist}** - **${title}** | **${duration}**`,
ephemeral: false
});
} catch (error) {
await interaction.reply('❌ Error playing song');
console.error(error);
}
}
// Handle the 'stop' command
if (commandName === 'stop') {
yukufy.stop();
await interaction.reply('🛑 Music stopped.');
}
// Handle the 'skip' command
if (commandName === 'skip') {
yukufy.skip();
await interaction.reply('⏭️ Song skipped.');
}
// Handle the 'pause' command
if (commandName === 'pause') {
const result = await yukufy.pause();
if (result.status === 'alreadyPaused') {
await interaction.reply('⏸️ Music is already paused.');
} else if (result.status === 'paused') {
await interaction.reply('⏸️ Music paused.');
}
}
// Handle the 'resume' command
if (commandName === 'resume') {
const result = await yukufy.resume();
if (result.status === 'alreadyPlaying') {
await interaction.reply('▶️ Music is already playing.');
} else if (result.status === 'resumed') {
await interaction.reply('▶️ Music resumed.');
}
}
// Handle the 'volume' command
if (commandName === 'volume') {
const volume = options.getNumber('number');
try {
yukufy.setVolume(volume);
await interaction.reply(`🔊 Volume set to: ${volume}`);
} catch (error) {
await interaction.reply('❌ Error adjusting volume');
console.error(error);
}
}
// Handle the 'search' command
if (commandName === 'search') {
const query = options.getString('query');
const source = options.getString('source');
try {
const results = await yukufy.search(query, source);
if (results && results.length > 0) {
const searchResults = results.map((r, index) => `${index + 1}. **${r.title}** by **${r.artist}**`).join('\n');
await interaction.reply(`🔍 Search results:\n${searchResults}`);
} else {
await interaction.reply('No results found.');
}
} catch (error) {
await interaction.reply('Error searching for songs.');
console.error(error);
}
}
// Handle the 'queue' command
if (commandName === 'queue') {
try {
const queue = await yukufy.getQueue();
if (queue.length === 0) {
await interaction.reply('The queue is empty.');
} else {
const queueString = queue.map(track => `${track.position}. ${track.title} - ${track.artist}`).join('\n');
await interaction.reply(`🎵 Song queue:\n${queueString}`);
}
} catch (error) {
console.error('Error getting the queue:', error);
await interaction.reply('An error occurred while trying to get the song queue.');
}
}
// Handle the 'join' command
if (commandName === 'join') {
const channel = interaction.member.voice.channel;
if (!channel) {
await interaction.reply('You need to be in a voice channel to invite me!');
return;
}
try {
await yukufy.join(channel);
await interaction.reply(`🔊 Joined the voice channel: ${channel.name}`);
} catch (error) {
await interaction.reply('❌ Error joining the voice channel');
console.error(error);
}
}
// Handle the 'leave' command
if (commandName === 'leave') {
try {
yukufy.leave(interaction.member.voice.channel.id);
await interaction.reply('👋 Left the voice channel.');
} catch (error) {
await interaction.reply('❌ Error leaving the voice channel');
console.error(error);
}
}
// Handle the 'loop' command
if (commandName === 'loop') {
try {
const loopOnOff = await yukufy.toggleLoop();
const statusMessage = loopOnOff ? 'enabled' : 'disabled';
await interaction.reply(`🔄 Loop is now ${statusMessage}`);
} catch (error) {
console.error('Error toggling loop:', error);
await interaction.reply('❌ There was an error trying to toggle the loop.');
}
}
// Handle the 'nowplaying' command
if (commandName === 'nowplaying') {
const nowPlaying = await yukufy.nowPlaying();
if (nowPlaying) {
const title = nowPlaying.title || 'Title not available';
const artist = nowPlaying.artist || 'Artist not available';
const url = nowPlaying.url || 'URL not available';
const duration = nowPlaying.duration || 'Duration not available';
const elapsedTime = nowPlaying.elapsedTime || 'Elapsed time not available';
await interaction.reply(`🎶 Now playing: **[${title}](${url})**\n🎤 Artist: **${artist}**\n⏱️ Duration: **${elapsedTime}**`);
} else {
await interaction.reply('No song is currently playing.');
}
}
// Handle the 'lyrics' command
if (commandName === 'lyrics') {
const query = options.getString('query');
try {
let searchQuery;
// If no query, try to get the currently playing song
if (!query) {
const nowPlaying = await yukufy.nowPlaying();
if (!nowPlaying) {
return await interaction.reply('No song playing at the moment and no search query provided.');
}
// Use the currently playing song for the lyrics search
searchQuery = `${nowPlaying.artist} ${nowPlaying.title}`;
} else {
// Use the provided query
searchQuery = query;
}
// Search for the lyrics
const lyrics = await yukufy.lyrics(searchQuery);
if (!lyrics) {
return await interaction.reply('Lyrics not found.');
}
// Check if lyrics exceed 2000 characters
if (lyrics.length > 2000) {
// Create a Buffer with the lyrics and send as a .txt file
const lyricsBuffer = Buffer.from(lyrics, 'utf-8');
await interaction.reply({
content: '🎤 Lyrics are too long, see the file:',
files: [{
attachment: lyricsBuffer,
name: 'lyrics.txt'
}]
});
} else {
// Send lyrics normally if under 2000 characters
await interaction.reply(`🎤 Lyrics:\n${lyrics}`);
}
} catch (error) {
await interaction.reply('❌ Error fetching lyrics.');
console.error(error);
}
}
});
// Event fired when a song starts playing
yukufy.on('playSong', ({ track }) => {
const { title, artist, url, duration, source, likes, thumbnail, member, textChannel, guildId } = track;
textChannel.send(`🎶 Now playing: **${artist} - ${title}** added by **${member.displayName}**`);
});
// Event fired when a song is added to the queue
yukufy.on('addSong', ({ track }) => {
const { title, artist, url, duration, source, likes, thumbnail, member, textChannel, guildId } = track;
textChannel.send(`🎵 Song **${artist} - ${title}** added to queue by **${member.displayName}**`);
});
// Event fired when the queue finishes
yukufy.on('finishQueue', ({ track }) => {
track.textChannel.send('🔚 Music queue finished.');
});
// Event fired when the queue becomes empty
yukufy.on('emptyQueue', ({ track }) => {
track.textChannel.send('I was waiting, no more songs were added to the queue, so I am leaving...');
});
// Event fired when the client disconnects from the voice channel
yukufy.on('clientDisconnect', () => {
console.log('👋 Disconnected from the voice channel.');
});
// Event fired when there is an error in the player
yukufy.on('playerError', () => {
console.log('Error');
});
// Log in to Discord with the bot token
await client.login(clientToken);
/* Uncomment this section for additional error handling
process.on("unhandledRejection", async (reason, promise) => {
console.log("[AntiCrash] | [UnhandledRejection_Logs] | [start] : ===============");
console.log("Unhandled Rejection at:", promise, "reason:", reason);
console.log("[AntiCrash] | [UnhandledRejection_Logs] | [end] : ===============");
});
process.on("uncaughtException", async (err, origin) => {
console.log("[AntiCrash] | [UncaughtException_Logs] | [Start] : ===============");
console.log(`Uncaught exception: ${err}\n` + `Exception origin: ${origin}`);
console.log("[AntiCrash] | [UncaughtException_Logs] | [End] : ===============");
});
process.on("uncaughtExceptionMonitor", async (err, origin) => {
console.log("[AntiCrash] | [UncaughtExceptionMonitor_Logs] | [Start] : ===============");
console.log(`Uncaught exception monitor: ${err}\n` + `Exception origin: ${origin}`);
console.log("[AntiCrash] | [UncaughtExceptionMonitor_Logs] | [End] : ===============");
});
*/
}
// Start the main function and handle errors
main().catch(console.error);
📢 Contributing
Contributions are welcome! Please fork the repository, make your changes, and submit a pull request. For major changes or new features, please open an issue to discuss them first.
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
👥 Support
If you have any questions or need help, join our Discord server for support and updates.
Enjoy managing your Discord music experience with Yukufy!