raftlink-apex
v1.0.0
Published
A Modern, Parallelized, Cloud-Native Audio System for Node.js
Readme
The Apex Philosophy
RAFTLINK: Apex Edition represents the peak of modern Lavalink client engineering, built on the latest stable technologies and advanced architectural patterns for unparalleled stability and performance.
True Parallelism via Actor Model: Every voice session is a completely isolated process (
worker_thread). This provides the ultimate level of fault tolerance and performance, as no single session can impact the stability of the main application or any other session. This is the gold standard for resilient systems.Predictable State Management: Inspired by modern frontend frameworks, each session actor manages its state immutably. Actions are dispatched to a reducer, ensuring predictable, debuggable, and race-condition-free state transitions.
Cloud-Native Persistence with MongoDB: All session data is persisted to a MongoDB Atlas cluster using the latest
mongodbv6 driver. This provides limitless scalability, robust data management, and the ability for your bot to run across multiple instances (shards) while sharing a single source of truth.Self-Healing & Intelligent Orchestration: The Cluster Manager actively monitors node health, performs zero-downtime session migrations, and automatically respawns crashed session actors, ensuring the system is constantly working to maintain a perfect state.
Core Features
- Actor Model Architecture: Unmatched performance and stability through true parallelism.
- Predictable State Management: Immutable state and Redux-like actions prevent bugs.
- Latest MongoDB v6 Integration: Professional-grade, scalable, and cloud-native persistence.
- Fault-Tolerant & Self-Healing: Automatic failover, session migration, and actor respawning.
- Geographic Load Balancing: Latency-based routing for optimal global performance.
- Cognitive Search: Intelligent query parsing finds what users mean.
- Advanced Player Controls: Rich queue management, looping, history, and a modern filter engine.
1. Project Setup
Installation
Install RAFTLINK and its peer dependencies from npm.
npm install raftlink-apex ws undici mongodb pino pino-prettyLavalink Server Configuration
This is the most crucial step. To unlock all-source support (Spotify, Apple Music, etc.), you must configure your Lavalink server's application.yml. RAFTLINK remains keyless; your Lavalink server holds the keys.
Example application.yml for Lavalink:
# application.yml on your Lavalink server
server:
port: 2333
lavalink:
server:
password: "youshallnotpass"
sources:
youtube: true
soundcloud: true
plugins:
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.0.0" # Example
plugins:
lavasrc:
spotify:
clientId: "YOUR_SPOTIFY_CLIENT_ID"
clientSecret: "YOUR_SPOTIFY_CLIENT_SECRET"2. Configuration
RAFTLINK is configured via a simple options object.
import { createApexClient } from 'raftlink-apex';
const apex = createApexClient({
userId: 'YOUR_BOT_ID_HERE',
sendGatewayPayload: (payload) => { /* Function to send data to Discord */ },
nodes: [ // Array of your Lavalink nodes
{
"identifier": "Lavalink-US-East",
"host": "us-east.lavalink.example.com",
"port": 443,
"password": "youshallnotpass",
"secure": true,
"region": "US_EAST"
}
],
database: {
uri: "YOUR_MONGODB_ATLAS_CONNECTION_STRING",
dbName: "RaftlinkApex"
},
logLevel: "info"
});3. Basic Usage
Here is a complete, minimal example using discord.js.
import { Client, GatewayIntentBits } from 'discord.js';
import { createApexClient } from 'raftlink-apex';
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates] });
const apex = createApexClient({
userId: 'YOUR_BOT_ID_HERE',
sendGatewayPayload: (payload) => {
client.guilds.cache.get(payload.d.guild_id)?.shard.send(payload);
},
nodes: [ /* Your nodes array from above */ ],
database: { /* Your database object from above */ }
});
async function main() {
await apex.connect(); // Connects to the database
client.on('ready', () => apex.logger.info(`Discord client logged in as ${client.user.tag}`));
// This is the critical link for voice functionality
client.on('raw', (d) => apex.handleVoiceUpdate(d.d));
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'play') return;
const { member, guild } = interaction;
if (!member.voice.channel) {
return interaction.reply({ content: 'You must be in a voice channel!', ephemeral: true });
}
await interaction.deferReply();
try {
const session = await apex.getPlayer(guild.id, member.voice.channel.id);
const query = interaction.options.getString('query');
const { tracks, loadType } = await session.loadTracks(query);
if (loadType === 'NO_MATCHES') return interaction.editReply('Could not find any results.');
await session.addToQueue(tracks);
const state = await session.getState();
if (!state.isPlaying) await session.play();
const reply = loadType === 'PLAYLIST_LOADED' ? `Queued **${tracks.length}** songs!` : `Queued: **${tracks[0].info.title}**`;
await interaction.editReply(reply);
} catch (error) {
apex.logger.error(error, `Error in play command`);
await interaction.editReply(`An error occurred: ${error.message}`);
}
});
client.login('YOUR_DISCORD_BOT_TOKEN_HERE');
}
main().catch(console.error);4. Mastering the Apex API
The SessionProxy object (returned by apex.getPlayer()) is the primary interface for all player-related actions.
Player Control
await session.play(): Starts or resumes playback. Automatically handles queue logic.await session.pause(): Toggles the pause state.await session.stop(): Stops playback and clears the queue.await session.skip(): Skips the current track.await session.setLoop('track'): Sets loop mode ('none', 'track', 'queue').await session.getState(): Retrieves the current state object ({ isPlaying, currentTrack, queue, ... }).
Queue Management
await session.addToQueue(tracks): Adds a track or array of tracks.await session.shuffleQueue(): Randomizes the queue.
Events
The SessionProxy is an EventEmitter.
session.on('trackStart', ({ track }) => {
console.log(`Track started: ${track.info.title}`);
});
session.on('queueEnd', () => {
console.log('The queue has finished.');
});