Skip to content

Configuration

import { RyanlinkManager } from 'ryanlink';
const manager = new RyanlinkManager({
// ── Required ──────────────────────────────────────────────────────────────
nodes: [
{
id: 'main',
host: 'localhost',
port: 2333,
authorization: 'youshallnotpass',
secure: false,
retryAmount: 5,
retryDelay: 10000,
retryTimespan: -1,
heartBeatInterval: 30000,
closeOnError: true,
enablePingOnStatsCheck: true,
autoChecks: {
pluginValidations: true,
sourcesValidations: true,
},
}
],
sendToShard: (guildId, payload) => {
// Send gateway payload to the correct shard
const totalShards = client.ws.shards.size || 1;
const shardId = Number((BigInt(guildId) >> 22n) % BigInt(totalShards));
const shard = client.ws.shards.get(shardId);
if (shard) shard.send(payload);
else client.guilds.cache.get(guildId)?.shard?.send(payload);
},
client: {
id: 'your-bot-id',
username: 'YourBot',
},
// ── Session Resuming ──────────────────────────────────────────────────────
resuming: {
enabled: true,
timeout: 60000,
},
// ── Player Defaults ───────────────────────────────────────────────────────
playerClass: CustomPlayer, // optional custom player class
autoSkip: true, // auto-skip on track end
autoSkipOnResolveError: true, // skip unresolvable tracks
trackResolveRetryLimit: 3, // global resolve retry limit
emitNewSongsOnly: false, // only emit trackStart for new songs
playerOptions: {
defaultSearchPlatform: 'ytsearch',
useUnresolvedData: false,
clientBasedPositionUpdateInterval: 100,
volumeDecrementer: 1,
applyVolumeAsFilter: false,
allowCustomSources: false,
requesterTransformer: (requester) => requester,
onDisconnect: {
destroyPlayer: true,
autoReconnect: false,
autoReconnectOnlyWithTracks: false,
},
onEmptyQueue: {
destroyAfterMs: undefined,
autoPlayFunction: async (player, lastTrack) => {
// your autoplay logic, or use the built-in:
// await Autoplay.defaultAutoplay(player, lastTrack);
},
},
autoplayConfig: {
enabled: true,
defaultSource: 'ytsearch',
limit: 1,
minDuration: 20000,
maxDuration: 900000,
durationTolerance: 90000,
historyLimit: 20,
prefetchThreshold: 1,
excludeKeywords: ['nightcore', 'bass boosted', '8d audio', 'slowed', 'reverb', 'bass boost', 'pitch shift', 'speed up', 'sped up'],
},
minAutoPlayMs: 10000,
enforceSponsorBlockRequestForEventEnablement: true,
trackResolveRetryLimit: 3,
onTrackStart: null,
onQueueEnd: null,
onNodeFailover: null,
},
// ── Queue ─────────────────────────────────────────────────────────────────
queueOptions: {
maxPreviousTracks: 25,
queueChangesWatcher: null,
},
// ── Links ─────────────────────────────────────────────────────────────────
linksAllowed: true,
linksWhitelist: [],
linksBlacklist: [],
// ── Advanced ──────────────────────────────────────────────────────────────
advancedOptions: {
enableDebugEvents: false,
maxFilterFixDuration: 600000,
debugOptions: {
logCustomSearches: false,
noAudio: false,
playerDestroy: {
dontThrowError: false,
debugLog: false,
},
},
},
});

OptionTypeDefaultDescription
idstringhost:portUnique node identifier
hoststringLavalink host
portnumberLavalink port (1–65535)
authorizationstringLavalink password
securebooleanfalseUse WSS/HTTPS (port must be 443)
sessionIdstringManual session ID for resuming
regionsstring[][]Voice regions this node serves (lowercased automatically)
nodeType'Core' | 'NodeLink''Core'Node type — use 'NodeLink' for NodeLink servers
retryAmountnumber5Max reconnect attempts
retryDelaynumber10000Base delay (ms) between reconnect attempts (exponential backoff)
retryTimespannumber-1Window (ms) to count retries; -1 = unlimited
requestSignalTimeoutMSnumber10000Abort timeout for REST requests
heartBeatIntervalnumber30000WebSocket heartbeat interval (ms)
closeOnErrorbooleantrueClose socket on error and force reconnect
enablePingOnStatsCheckbooleantrueTrigger heartbeat on every stats event
autoChecks.pluginValidationsbooleantrueValidate plugin names before requests
autoChecks.sourcesValidationsbooleantrueValidate source managers before requests

OptionTypeDefaultDescription
defaultSearchPlatformstring'ytsearch'Default search prefix for plain queries
useUnresolvedDatabooleanfalsePrefer unresolved track metadata over resolved
clientBasedPositionUpdateIntervalnumber100Client-side position update interval (ms)
volumeDecrementernumber1Multiplier applied to internal volume (e.g. 0.75 = 75% max)
applyVolumeAsFilterbooleanfalseApply volume as a filter instead of player volume
allowCustomSourcesbooleanfalseSkip source validation for unknown search prefixes
minAutoPlayMsnumber10000Minimum ms between autoplay triggers
trackResolveRetryLimitnumber3Per-player resolve retry limit
enforceSponsorBlockRequestForEventEnablementbooleantrueAuto-call setSponsorBlock on track start
onDisconnect.destroyPlayerbooleantrueDestroy player on voice disconnect
onDisconnect.autoReconnectbooleanfalseAuto-reconnect on voice disconnect
onDisconnect.autoReconnectOnlyWithTracksbooleanfalseOnly reconnect if queue has tracks
onEmptyQueue.destroyAfterMsnumberundefinedDestroy player N ms after queue empties
onTrackStartfunction | nullnullPer-player hook called on every track start
onQueueEndfunction | nullnullPer-player hook called when queue empties
onNodeFailoverfunction | nullnullPer-player hook called when node changes

const player = manager.createPlayer({
guildId: '123456789',
voiceChannelId: '987654321',
textChannelId: '111222333', // optional
selfDeaf: true,
selfMute: false,
volume: 100, // 0–1000
node: 'main', // optional: pin to specific node id
vcRegion: 'us-east', // optional: prefer nodes in this region
autoplay: false,
recentHistory: [], // optional: pre-populate history buffer
customData: {}, // optional: arbitrary key-value data
trackResolveRetryLimit: 3,
smartLeave: false, // destroy player when voice channel empties
autoPause: false, // pause when all users leave, resume when they return
autoPauseOnMute: false, // pause when bot is muted
onTrackStart: (p, track) => {},
onQueueEnd: (p) => {},
onNodeFailover: (p, from, to) => {},
});
await player.connect();

// Basic search via manager
const result = await manager.search({ query: 'never gonna give you up', source: 'ytsearch' }, requester);
// Via player (also supports bandcamp local engine)
const result2 = await player.search('spsearch:daft punk', requester);
// LavaSearch (requires lavasearch-plugin)
const rich = await player.audioSearch({
query: 'daft punk',
source: 'spsearch',
types: ['track', 'album', 'artist'],
}, requester);

OptionTypeDefaultDescription
maxPreviousTracksnumber25Size of the previous tracks buffer
queueChangesWatcherQueueChangesWatcher | nullnullCallback interface for queue mutations

Implement this interface to react to queue changes:

const manager = new RyanlinkManager({
queueOptions: {
queueChangesWatcher: {
tracksAdd(guildId, tracks, position, oldQueue, newQueue) {
console.log(`[${guildId}] Added ${tracks.length} track(s) at position ${position}`);
},
tracksRemoved(guildId, tracks, position, oldQueue, newQueue) {
console.log(`[${guildId}] Removed ${tracks.length} track(s) from position ${position}`);
},
shuffled(guildId, oldQueue, newQueue) {
console.log(`[${guildId}] Queue shuffled`);
},
},
},
});

Always implement sendToShard correctly for your Discord library. Without it, Ryanlink cannot communicate with Discord’s voice servers.