Configuration
Manager Configuration
Section titled “Manager 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, }, }, },});Node Configuration
Section titled “Node Configuration”| Option | Type | Default | Description |
|---|---|---|---|
id | string | host:port | Unique node identifier |
host | string | — | Lavalink host |
port | number | — | Lavalink port (1–65535) |
authorization | string | — | Lavalink password |
secure | boolean | false | Use WSS/HTTPS (port must be 443) |
sessionId | string | — | Manual session ID for resuming |
regions | string[] | [] | Voice regions this node serves (lowercased automatically) |
nodeType | 'Core' | 'NodeLink' | 'Core' | Node type — use 'NodeLink' for NodeLink servers |
retryAmount | number | 5 | Max reconnect attempts |
retryDelay | number | 10000 | Base delay (ms) between reconnect attempts (exponential backoff) |
retryTimespan | number | -1 | Window (ms) to count retries; -1 = unlimited |
requestSignalTimeoutMS | number | 10000 | Abort timeout for REST requests |
heartBeatInterval | number | 30000 | WebSocket heartbeat interval (ms) |
closeOnError | boolean | true | Close socket on error and force reconnect |
enablePingOnStatsCheck | boolean | true | Trigger heartbeat on every stats event |
autoChecks.pluginValidations | boolean | true | Validate plugin names before requests |
autoChecks.sourcesValidations | boolean | true | Validate source managers before requests |
Player Options
Section titled “Player Options”| Option | Type | Default | Description |
|---|---|---|---|
defaultSearchPlatform | string | 'ytsearch' | Default search prefix for plain queries |
useUnresolvedData | boolean | false | Prefer unresolved track metadata over resolved |
clientBasedPositionUpdateInterval | number | 100 | Client-side position update interval (ms) |
volumeDecrementer | number | 1 | Multiplier applied to internal volume (e.g. 0.75 = 75% max) |
applyVolumeAsFilter | boolean | false | Apply volume as a filter instead of player volume |
allowCustomSources | boolean | false | Skip source validation for unknown search prefixes |
minAutoPlayMs | number | 10000 | Minimum ms between autoplay triggers |
trackResolveRetryLimit | number | 3 | Per-player resolve retry limit |
enforceSponsorBlockRequestForEventEnablement | boolean | true | Auto-call setSponsorBlock on track start |
onDisconnect.destroyPlayer | boolean | true | Destroy player on voice disconnect |
onDisconnect.autoReconnect | boolean | false | Auto-reconnect on voice disconnect |
onDisconnect.autoReconnectOnlyWithTracks | boolean | false | Only reconnect if queue has tracks |
onEmptyQueue.destroyAfterMs | number | undefined | Destroy player N ms after queue empties |
onTrackStart | function | null | null | Per-player hook called on every track start |
onQueueEnd | function | null | null | Per-player hook called when queue empties |
onNodeFailover | function | null | null | Per-player hook called when node changes |
Player Creation
Section titled “Player Creation”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();Search
Section titled “Search”// Basic search via managerconst 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);Queue Options
Section titled “Queue Options”| Option | Type | Default | Description |
|---|---|---|---|
maxPreviousTracks | number | 25 | Size of the previous tracks buffer |
queueChangesWatcher | QueueChangesWatcher | null | null | Callback interface for queue mutations |
QueueChangesWatcher
Section titled “QueueChangesWatcher”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
sendToShardcorrectly for your Discord library. Without it, Ryanlink cannot communicate with Discord’s voice servers.