npm versiom MIT License

A powerful and efficient Lavalink client for Discord.js, designed for high performance and ease of use.

High Performance

Optimized for speed and efficiency

Easy Integration

Seamless Discord.js integration

Reliable

Built for stability and resilience

Installation

npm
npm install aqualink

Basic Setup

Quick example of setting up AquaLink with Discord.js:

JavaScript
const { Client } = require('discord.js');
            const { Aqua } = require('aqualink');
            
            const client = new Client({
                intents: ['Guilds', 'GuildVoiceStates']
            });
            
            client.aqua = new Aqua(client, nodes, {
                nodes: [{
                    host: 'localhost',
                    port: 2333,
                    password: 'youshallnotpass'
                }],
                send: (guildId, payload) => {
                    const guild = client.guilds.cache.get(guildId);
                    if (guild) guild.shard.send(payload);
                }
            });

Initialization

Create an initialization file to set up AquaLink events:

JavaScript: events/aqua/init.js

            const { client_id } = require("../../../config.js"); // change to where your client_id is located.
            const { logger } = require("../../../utils/logger.js"); // change to what you have for logging.
            const client = require("../../../index.js"); // change the location of where your index/bot.js is located.
            
            module.exports = async () => {
                client.aqua.init(client_id)
                logger(`Successfully Initiated Aqualink Events`, "debug");
            };

Make sure to have your client ID in the config file and a logger utility set up for proper initialization.

Quick Start

Here's a simple example of playing music:

JavaScript

            client.on('interactionCreate', async interaction => {
                if (!interaction.isCommand()) return;
            
                if (interaction.commandName === 'play') {
                    const query = interaction.options.getString('song');
                    const voiceChannel = interaction.member.voice.channel;
            
                    if (!voiceChannel) {
                        return interaction.reply('You need to be in a voice channel!');
                    }
            
                    try {
                        const player = aqua.createPlayer({
                            guildId: interaction.guildId,
                            voiceChannel: voiceChannel.id,
                            textChannel: interaction.channelId
                        });
            
                        const result = await aqua.search(query);
                        if (!result.tracks.length) {
                            return interaction.reply('No results found!');
                        }
            
                        player.queue.add(result.tracks[0]);
                        if (!player.playing) await player.play();
            
                        await interaction.reply(`Added ${result.tracks[0].title} to the queue!`);
                    } catch (error) {
                        console.error(error);
                        await interaction.reply('An error occurred!');
                    }
                }
            });

Make sure to have your client ID in the config file and a logger utility set up for proper initialization. This example assumes you have already set up slash commands in your Discord application.

Player Class

Handles music playback and track management.

Methods

.play()

Starts playing the current track in the queue.

await player.play();

.pause() / .resume()

Controls playback state.

player.pause(true);
player.pause(false);

.stop()

Stops the current track and clears the queue.

player.stop();

Playing a Track

Here's how to play a track using AquaLink, including automatic player creation if needed:

JavaScript
async function playTrack(guildId, track) {
                let player = client.aqua.players.get(interaction.guildId);
                if (!player) {
                    player = client.aqua.createConnection({
                        defaultVolume: 65,
                        guildId: interaction.guildId,
                        voiceChannel: interaction.member.voice.channelId,
                        textChannel: interaction.channelId,
                        deaf: true
                    });
                }
                player.play(track);
            }

This function checks for an existing player and creates one if needed. The player is configured with default settings including volume level and automatic deafening of the bot.

Parameters

  • guildId - The ID of the guild (server)
  • track - The track object to play

Player Options

  • defaultVolume - Initial volume level (0-100)
  • guildId - The guild ID for the connection
  • voiceChannel - The voice channel ID to connect to
  • textChannel - The text channel ID for notifications
  • deaf - Whether the bot should be deafened

Example of Searching for a Track Using Lavalink

Here's how to search for tracks and add them to the queue using AquaLink's resolve method:

JavaScript
async function searchAndPlay(guildId, query) {
                const resolve = await client.aqua.resolve({
                    query,
                    requester: interaction.member, // optional!
                    source: "scsearch"
                });
                if (!results.tracks.length) return console.log("No results found.");
            
                const track = results.tracks[0]; 
                await player.queue.add(track)
                if (!player.playing && !player.paused && player.queue.size > 0) {
                    player.play();
                }
            }

The resolve method supports different search sources like 'ytsearch' (YouTube), 'scsearch' (SoundCloud), and direct URLs. The requester field is optional but useful for tracking who requested each track.

Parameters

  • guildId - The ID of the guild (server)
  • query - The search query or URL

Resolve Options

  • query - The search term or URL
  • requester - The user who requested the track (optional)
  • source - The search source (ytsearch, scsearch, etc.)

Supported Sources

  • ytsearch - YouTube search
  • scsearch - SoundCloud search
  • dzsearch - Deezer search
  • Direct URLs from supported platforms

Controlling Playback

Here are various methods to control music playback using AquaLink:

Pausing and Resuming

JavaScript
async function pausePlayback(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) player.pause(true);
            }
            
            async function resumePlayback(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) player.pause(false);
            }

Skipping a Track

JavaScript
async function skipTrack(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) player.stop();
            }

Stopping and Disconnecting

JavaScript
async function stopPlayback(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) player.stop();
            }
            
            async function disconnectPlayer(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) player.destroy();
            }

All these functions first check if a player exists for the guild before attempting to control playback. The destroy() method will completely remove the player instance and disconnect from the voice channel.

Available Methods

  • pause(boolean) - Pause or resume playback
  • stop() - Stop current track and move to next in queue
  • destroy() - Disconnect and cleanup player instance

Parameters

  • guildId - The ID of the guild (server)
  • pause(true/false) - True to pause, false to resume

Queue Management

Here are methods to manage the track queue in AquaLink:

Adding a Track to the Queue

JavaScript
async function addToQueue(guildId, track) {
                const player = client.aqua.players.get(interaction.guildId);
                if (!player.queue) player.queue = [];
            
                player.queue.push(track);
                if (!player.playing) playTrack(guildId, player.queue.shift());
            }

Viewing the Current Queue

JavaScript
async function viewQueue(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (!player || !player.queue.length) return console.log("The queue is empty.");
            
                console.log("Current Queue:");
                player.queue.forEach((track, index) => {
                    console.log(`${index + 1}. ${track.info.title}`);
                });
            }

Looping a Track

JavaScript
async function loopTrack(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (!player) return;
            
                player.queue.unshift(player.queue[player.queue.length - 1]); 
            }

Queue management functions handle the playlist of tracks. The queue is maintained per guild, and tracks are automatically played in sequence. The loop function adds the current track back to the beginning of the queue for continuous playback.

Queue Methods

  • push(track) - Add a track to the end of queue
  • shift() - Remove and return the first track
  • unshift(track) - Add a track to the beginning
  • forEach(callback) - Iterate through the queue

Track Properties

  • track.info.title - The track's title
  • track.info.author - The track's author
  • track.info.length - Duration in milliseconds
  • track.info.uri - Source URL of the track

Disconnecting and Cleanup

To ensure your bot properly disconnects and cleans up resources, use the following:

JavaScript
async function cleanup(guildId) {
                const player = client.aqua.players.get(interaction.guildId);
                if (player) {
                    player.stop();
                    player.destroy();
                }
            }

Always ensure proper cleanup when disconnecting your bot to prevent resource leaks and hanging voice connections. This is especially important when handling bot shutdowns or voice channel leave events.

Cleanup Steps

  • player.stop() - Stops any current playback
  • player.destroy() - Destroys the player instance and disconnects from voice

Usage Examples

  • Bot shutdown events
  • Voice channel leave events
  • Guild leave events
  • Manual disconnect commands

Handling Voice State Updates

AquaLink requires voice state updates to maintain connections. Ensure you handle these events properly:

JavaScript
client.on("raw", (d) => {
                if (![GatewayDispatchEvents.VoiceStateUpdate, GatewayDispatchEvents.VoiceServerUpdate].includes(d.t)) return;
                client.aqua.updateVoiceState(d);
            });

This event handler is crucial for maintaining stable voice connections. It processes voice state and server updates from Discord and passes them to AquaLink for proper connection management.

Connecting to a Voice Channel

Here's how to connect your bot to a voice channel using AquaLink:

JavaScript
async function connectToChannel(guildId, channelId) {
                const player = client.aqua.players.get(interaction.guildId);
                player.connect(channelId);
            }

The player instance handles the voice connection. Make sure you have a valid player instance before attempting to connect, and ensure the bot has the necessary permissions in the voice channel.

Parameters

  • guildId - The ID of the guild (server)
  • channelId - The ID of the voice channel to connect to

Queue Class

Manages the track queue system.

Methods

.add(track)

Adds a track to the queue.

player.queue.add(track);

.remove(index)

Removes a track from the queue.

player.queue.remove(0);

.shuffle()

Shuffles the current queue.

player.queue.shuffle();

Node Class

Manages Lavalink node connections.

Properties

.stats

Current node statistics.

console.log(node.stats);

Audio Filters

Apply audio effects and filters to your music.

Equalizer

player.filters.setTremolo(true); ex: ({
    equalizer: [
        { band: 0, gain: 0.6 },
        { band: 1, gain: 0.7 },
        { band: 2, gain: 0.8 }
    ]
});

Karaoke

player.setFilter({
    karaoke: {
        level: 1.0,
        monoLevel: 1.0,
        filterBand: 220.0,
        filterWidth: 100.0
    }
});

Events

Handle various player and track events.

trackStart

client.aqua.on('trackStart', (track) => {
    console.log(`Now playing: ${track.info.title}`);
});

queueEnd

client.aqua.on('queueEnd', () => {
    console.log('Queue has ended!');
});

Events

Handle various node, player, and track events in AquaLink.

Node Events

Events related to Lavalink node connections.

Node Events
// Node connection event ex: const client = require("../../../index.js");
            client.aqua.on('nodeConnect', (node) => {
                console.log(`Node ${node.host} connected`);
            });
            
            client.aqua.on('nodeDisconnect', (node, reason) => {
                console.log(`Node disconnected: ${reason}`);
            });
            
            client.aqua.on('nodeError', (node, error) => {
                console.error(`Node ${node.host} had an error: ${error.message}`);
            });

Player Events

Events related to player lifecycle.

Player Events
// the client is your index.js ex: const client = require("../../../index.js");
            client.aqua.on('playerCreate', (player) => {
                console.log(`Player created in guild: ${player.guildId}`);
            });
            
            client.aqua.on('playerDestroy', (player) => {
                console.log(`Player destroyed in guild: ${player.guildId}`);
            });
            
            client.aqua.on('queueEnd', (player) => {
                console.log(`Queue ended in guild: ${player.guildId}`);
                setTimeout(() => {
                    if (!player.playing) player.disconnect();
                }, 300000); 
            });

Track Events

Events related to track playback.

Track Events

            client.aqua.on('trackStart', (track) => {
                console.log(`Now playing: ${track.title}`);
            });
            
            client.aqua.on('trackEnd', (track, reason) => {
                console.log(`Track ${track.title} ended: ${reason}`);
            });
            
            client.aqua.on('trackError', (track, error) => {
                console.error(`Error playing ${track.title}: ${error.message}`);
            });
            
            client.aqua.on('trackStuck', (track, threshold) => {
                console.warn(`Track ${track.title} got stuck (threshold: ${threshold}ms)`);
                player.skip(); 
            });

Troubleshooting

Common issues and their solutions when working with AquaLink:

No Sound or Bot Not Joining

  • ✓ Ensure the Lavalink server is running and accessible.
  • ✓ Verify the Lavalink password and port are correct in your code.
  • ✓ Ensure your bot has the correct permissions (CONNECT and SPEAK).
// Example permissions check
            if (!voiceChannel.permissionsFor(client.user).has(['CONNECT', 'SPEAK'])) {
                return console.log('Missing required voice permissions!');
            }

Bot Leaves the Channel Unexpectedly

  • ✓ The bot may be idle for too long. Consider implementing an inactivity timeout.
  • ✓ Lavalink may be crashing—check your Lavalink logs.
// Example inactivity timeout
            client.on('queueEnd', (player) => {
                setTimeout(() => {
                    if (!player.playing) player.destroy();
                }, 300000); // 5 minutes
            });

Music Skipping or Lagging

  • ✓ Check your server's CPU and memory usage. Lavalink requires adequate resources.
  • ✓ If hosting Lavalink remotely, ensure the network connection is stable.

Recommended minimum specs for Lavalink: 2 CPU cores, 512MB RAM

If issues persist, check the Lavalink server logs for detailed error messages. You can also enable debug mode in AquaLink for more detailed logging:

client.aqua = new Aqua(client, nodes, {
                debug: true,
                // ... other options
            });

Common Error Codes

  • ECONNREFUSED - Lavalink server not accessible
  • AuthenticationFailed - Incorrect Lavalink password
  • TrackStuck - Track playback issues
  • LoadFailed - Unable to load track

Required Permissions

  • CONNECT - Join voice channels
  • SPEAK - Play audio in voice channels
  • VIEW_CHANNEL - See the voice channel
  • USE_VAD - Use voice activity

Error Handling

Handle and manage errors effectively.

Error Events

client.aqua.on('error', (error) => {
    console.error('Player error:', error);
});

client.aqua.on('nodeError', (node, error) => {
    console.error(`Node ${node.host} error:`, error);
});