import { LavalinkManager, QueueChangesWatcher, QueueStoreManager } from "lavalink-client";
import { RedisClientType } from "redis"; // example for custom queue store
import { Client, GatewayIntentBits } from "discord.js"; // example for a discord bot
// you might want to extend the types of the client, to bind lavalink to it.
const client = new Client({
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
const previouslyUsedSessions = new Map<string, string>(); //nodeManager.on("connect", node => previouslyUsedSessions.set(node.id, node.sessionId))
client.lavalink = new LavalinkManager({
authorization: "localhoist",
// get the previously used session, to restart with "resuming" enabled
sessionId: previouslyUsedSessions.get("testnode"),
requestSignalTimeoutMS: 3000,
heartBeatInterval: 30_000,
enablePingOnStatsCheck: true,
sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload),
client: { // client: client.user
id: envConfig.clientId, // REQUIRED! (at least after the .init)
autoSkipOnResolveError: true, // skip song, if resolving an unresolved song fails
emitNewSongsOnly: true, // don't emit "looping songs"
// These are the default prevention methods
// only allow an autoplay function to execute, if the previous function was longer ago than this number.
applyVolumeAsFilter: false,
clientBasedPositionUpdateInterval: 50, // in ms to up-calc player.position
defaultSearchPlatform: "ytmsearch",
volumeDecrementer: 0.75, // on client 100% == on lavalink 75%
requesterTransformer: requesterTransformer,
autoReconnect: true, // automatically attempts a reconnect, if the bot disconnects from the voice channel, if it fails, it get's destroyed
destroyPlayer: false // overrides autoReconnect and directly destroys the player if the bot disconnects from the vc
// will auto destroy the player after 30s if the queue got empty and autoplay function does not add smt to the queue
destroyAfterMs: 30_000, // 1 === instantly destroy | don't provide the option, to don't destroy the player
autoPlayFunction: autoPlayFunction,
// only needed if you want and need external storage, don't provide if you don't need to
queueStore: new myCustomStore(client.redis), // client.redis = new redis()
// only needed, if you want to watch changes in the queue via a custom class,
queueChangesWatcher: new myCustomWatcher(client)
// example: don't allow p*rn / youtube links., you can also use a regex pattern if you want.
// linksBlacklist: ["porn", "youtube.com", "youtu.be"],
maxFilterFixDuration: 600_000, // only allow instafixfilterupdate for tracks sub 10mins
logCustomSearches: false,
// IMPORTANT- Send voice data to lavalink
client.on("raw", d => client.lavalink.sendRawData(d));
client.on("ready", () => {
client.lavalink.init(client.user); // init lavalink
// for the custom queue Store create a redis instance
client.redis = createClient({ url: "redis://localhost:6379", password: "securepass" });
// Custom external queue Store
// if queueStore is not provided on default it uses MiniMap
export class myCustomStore implements QueueStoreManager {
private redis:RedisClientType;
constructor(redisClient:RedisClientType) {
this.redis = redisClient;
async get(guildId): Promise<any> {
return await this.redis.get(this.id(guildId));
async set(guildId, stringifiedQueueData): Promise<any> {
return await this.redis.set(this.id(guildId), stringifiedQueueData);
async delete(guildId): Promise<any> {
return await this.redis.del(this.id(guildId));
async parse(stringifiedQueueData): Promise<Partial<StoredQueue>> {
return JSON.parse(stringifiedQueueData);
async stringify(parsedQueueData): Promise<any> {
return JSON.stringify(parsedQueueData);
return `lavalinkqueue_${guildId}`; // transform the id to your belikings
// Custom Queue Watcher Functions
export class myCustomWatcher implements QueueChangesWatcher {
shuffled(guildId, oldStoredQueue, newStoredQueue) {
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: Queue got shuffled`)
tracksAdd(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got added into the Queue at position #${position}`);
tracksRemoved(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
console.log(`${this.client.guilds.cache.get(guildId)?.name || guildId}: ${tracks.length} Tracks got removed from the Queue at position #${position}`);