nerox-llclient
Start here
Section titled “Start here”Why this client
Section titled “Why this client”- Smart node selection with region preference, load balancing, priority, health, or custom policies.
- A typed player API with automatic queue progression, repeat modes, unresolved tracks, filters, lyrics, and plugin events.
- Persistent queue stores and queue change watchers for multi-process bots.
- Lavalink v4 and NodeLink support without forcing a framework or Discord library.
- Clear errors, debug events, source validation, and safe link allowlists/denylists.
nerox-llclient
Fast, typed Lavalink v4 and NodeLink client for Discord music bots.
nerox-llclient gives you a small, framework-agnostic player API with smart
node selection, resilient reconnects, persistent queues, filters, lyrics,
SponsorBlock, and typed events. It works with discord.js, Oceanic, Eris, or a
custom gateway client.
Why use it
- Smart node selection — balance by players, active players, memory, CPU, REST calls, priority, health, voice region, or your own selector.
- Resilient playback — automatic queue progression, reconnects, failover, autoplay hooks, repeat modes, and error thresholds.
- Fast transitions — in-flight resolution deduplication, background prefetching, serialized queue persistence, and guarded track transitions.
- Reliable sessions — stale websocket protection, healthy-handshake retry handling, and voice-region reconnect support for long-running bots.
- Persistent queues — use the fast in-memory store or plug in Redis, PostgreSQL, a database, or any async store.
- Lavalink-native — v4 REST/WebSocket support, plugin validation, SponsorBlock, lyrics, filters, and NodeLink features.
- Easy to extend — custom player classes, queue watchers, requester transformers, search sources, link allowlists, and typed events.
- No framework lock-in — the only Discord-specific callback is forwarding voice state payloads to your shard.
Requirements
- Node.js 18 or newer, or Bun 1.1.27 or newer
- Lavalink v4 or NodeLink
- A Discord bot with the Guild Voice States intent
Install
npm install nerox-llclient# or: pnpm add nerox-llclient# or: yarn add nerox-llclient# or: bun add nerox-llclientQuick start
import { LavalinkManager } from "nerox-llclient";
const lavalink = new LavalinkManager({ nodes: [ { id: "primary", host: process.env.LAVALINK_HOST ?? "localhost", port: Number(process.env.LAVALINK_PORT ?? 2333), authorization: process.env.LAVALINK_PASSWORD ?? "youshallnotpass", regions: ["asia", "us"], priority: 10, }, ], nodeSelection: { strategy: "health", }, sendToShard: (guildId, payload) => { // Forward this payload through your Discord library. discordClient.ws.send(guildId, payload); }, client: { id: process.env.CLIENT_ID!, username: "my-music-bot", }, playerOptions: { onDisconnect: { autoReconnect: true, destroyPlayer: false }, onEmptyQueue: { destroyAfterMs: 30_000 }, },});
discordClient.on("raw", (packet) => void lavalink.sendRawData(packet));discordClient.once("ready", async () => { await lavalink.init({ id: discordClient.user.id, username: discordClient.user.username, });});Create a player, search, queue, and play:
const player = lavalink.createPlayer({ guildId, voiceChannelId, textChannelId, selfDeaf: true,});
await player.connect();
const result = await player.search({ query: "Daft Punk Get Lucky" }, requester, true);await player.queue.add(result.tracks.slice(0, 10));await player.play();The queue includes convenient size, isEmpty, and duration properties,
plus filtering, shuffling, repeat modes, previous tracks, unresolved tracks,
and persistence hooks.
Node selection
Use a manager default:
const lavalink = new LavalinkManager({ // ... nodeSelection: { strategy: "priority", region: "asia", },});Or select explicitly:
const node = lavalink.getBestNode({ strategy: "health", region: "us",});
const player = lavalink.createPlayer({ guildId, voiceChannelId, node });Available built-in strategies:
| Strategy | Best for |
|---|---|
players | General-purpose balancing |
playingPlayers | Keeping active playback evenly distributed |
memory | Nodes with different memory capacity |
cpuLavalink / cpuSystem | CPU-sensitive deployments |
calls | Distributing REST traffic |
priority | Primary and backup nodes |
health | Mixed hardware and automatic health scoring |
custom | Application-specific policies |
Queues and storage
The default queue store is in-memory. For multi-process bots, implement
QueueStoreManager and pass it to queueOptions.queueStore. Queue changes can
also be observed through queueOptions.queueChangesWatcher.
await player.queue.add(tracks);await player.queue.remove(2);await player.queue.shuffle();
const longSongs = player.queue.utils.filterTracks({ duration: { min: 5 * 60_000 },});Events
lavalink.on("trackStart", (player, track) => { console.log(`${player.guildId}: ${track?.info.title}`);});
lavalink.on("queueEnd", (player) => { console.log(`Queue finished in ${player.guildId}`);});
lavalink.on("debug", (key, data) => { if (data.state === "error") console.error(key, data.error);});Development
npm installnpm run buildnpm run lintThe package build outputs CommonJS, ESM, and declaration files in dist/.
The documentation site is in docs/ and uses Astro Starlight with generated
TypeDoc API pages. The testBot/ directory contains a full Discord example;
it needs a Discord token and a reachable Lavalink server.
Documentation
License
MIT