Next-gen Lavalink client with ultra-low latency, smart node balancing, and 25+ audio effects.
- Installation
- Quick Start
- Core Classes
- Feature Details
- Audio Processing
- Network
- Plugins
- Events
- TypeScript
npm install pappifyDependencies:
ws- WebSocket clientundici- HTTP client
Peer Dependencies (optional):
discord.js^14.x
const { Client, GatewayIntentBits } = require('discord.js');
const { Pappify } = require('pappify');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
const pappify = new Pappify(client, [
{
name: 'main',
host: 'localhost',
port: 2333,
password: 'youshallnotpass',
}
], {
send: (data) => client.guilds.cache.get(data.d.guild_id)?.shard.send(data),
defaultSearchPlatform: 'ytmsearch',
restVersion: 'v4',
});
client.on('ready', () => {
pappify.init(client.user.id);
});
client.on('raw', (d) => pappify.updateVoiceState(d));
client.login('YOUR_TOKEN');Main client class for managing Lavalink connections.
new Pappify(client, nodes, options)| Parameter | Type | Description |
|---|---|---|
client |
Object | Discord client instance |
nodes |
Array | Array of node configurations |
options |
Object | Pappify options |
| Option | Type | Default | Description |
|---|---|---|---|
send |
Function | required | Function to send voice data to Discord |
defaultSearchPlatform |
string | 'ytmsearch' |
Default search platform |
restVersion |
string | 'v4' |
Lavalink REST version ('v3' or 'v4') |
autoMigratePlayers |
boolean | false |
Auto-migrate players on node issues |
migrateOnDisconnect |
boolean | false |
Migrate players when node disconnects |
migrateOnFailure |
boolean | false |
Migrate players on node failure |
migrationStrategyFn |
Function | default | Custom migration strategy |
resumeKey |
string | null |
Session resume key |
resumeTimeout |
number | 60 |
Resume timeout in seconds |
reconnectTimeout |
number | 5000 |
Reconnect delay in ms |
reconnectTries |
number | 5 |
Max reconnect attempts |
plugins |
Array | [] |
Array of plugins |
multipleTrackHistory |
number | null |
Max previous tracks to store |
Pappify allows you to enable or disable specific features. All features are enabled by default.
| Feature | Type | Default | Description |
|---|---|---|---|
autoplay |
boolean | true |
Enable autoplay when queue ends |
filters |
boolean | true |
Enable audio filters (bassboost, nightcore, etc.) |
effects |
boolean | true |
Enable audio effects (lofi, underwater, etc.) |
voiceEffects |
boolean | true |
Enable voice manipulation (robot, demon, etc.) |
tts |
boolean | true |
Enable text-to-speech |
lyrics |
boolean | true |
Enable lyrics fetching |
mixer |
boolean | true |
Enable audio mixing (NodeLink only) |
recorder |
boolean | true |
Enable voice recording (NodeLink only) |
spotify |
boolean | true |
Enable Spotify URL resolution |
Automatically plays similar tracks when the queue ends.
How it works:
- When queue ends and autoplay is enabled, Pappify finds related tracks based on the last played song
- Supports YouTube, SoundCloud, and Spotify sources
- Uses YouTube Mix/Radio for YouTube tracks
- Uses recommended tracks for SoundCloud
- Cross-searches between platforms for Spotify
- Tracks played identifiers to avoid repeating songs
Usage:
// Enable autoplay
await player.autoplay(player);
// Disable autoplay
await player.autoplay(false);
// Check status
console.log(player.isAutoplay); // true/falseWhen disabled: player.autoplay() throws Error: Autoplay feature is disabled
Audio filters that modify sound characteristics using Lavalink's built-in filters.
How it works:
- Sends filter configurations to Lavalink via REST API
- Filters are applied server-side for low latency
- Multiple filters can be combined
- Changes apply in real-time to current playback
Available filters:
| Filter | Description | Parameters |
|---|---|---|
bassboost |
Boost bass frequencies | value: 0-10 |
nightcore |
Speed up + higher pitch | - |
vaporwave |
Slow down + lower pitch | - |
8D |
Rotating audio effect | rotationHz: 0.1-1.0 |
karaoke |
Remove vocals | level, monoLevel, filterBand, filterWidth |
tremolo |
Volume oscillation | frequency, depth |
vibrato |
Pitch oscillation | frequency, depth |
rotation |
Audio rotation | rotationHz |
distortion |
Distort audio | sinOffset, sinScale, cosOffset, cosScale, tanOffset, tanScale, offset, scale |
lowPass |
Remove high frequencies | smoothing |
channelMix |
Mix left/right channels | leftToLeft, leftToRight, rightToLeft, rightToRight |
timescale |
Speed/pitch/rate control | speed, pitch, rate |
equalizer |
15-band EQ | bands: [{band, gain}] |
slowmode |
Slow playback | - |
daycore |
Opposite of nightcore | - |
chipmunk |
High pitch voice | - |
darthvader |
Deep voice | - |
doubletime |
2x speed | - |
pop |
Pop music preset | - |
soft |
Soft audio preset | - |
trebleBass |
Boost treble and bass | - |
Usage:
// Single filter
player.filters.setBassboost(true, { value: 8 });
player.filters.setNightcore(true);
player.filters.set8D(true);
// Custom EQ
player.filters.setEqualizer([
{ band: 0, gain: 0.6 }, // 25 Hz
{ band: 1, gain: 0.5 }, // 40 Hz
{ band: 2, gain: 0.3 }, // 63 Hz
]);
// Custom timescale
player.filters.setTimescale(true, {
speed: 1.2, // 20% faster
pitch: 1.1, // 10% higher pitch
rate: 1.0, // Normal rate
});
// Clear all filters
await player.filters.clearFilters();
// Get active filters
console.log(player.filters.active); // ['bassboost', 'nightcore']When disabled: All player.filters methods throw Error: Feature "filters" is disabled
Pre-configured audio effect combinations for specific sounds.
How it works:
- Combines multiple filters to create complex effects
- Each effect has optimized settings for best sound
- Effects can be stacked (some combinations work better than others)
- Uses filters internally but provides simpler API
Available effects:
| Effect | Description | What it does |
|---|---|---|
lofi |
Lo-fi aesthetic | Low pass filter + slight bass boost |
underwater |
Underwater sound | Heavy low pass + tremolo |
telephone |
Phone call quality | Band pass filter (300-3400 Hz) |
radio |
AM radio sound | Distortion + band limiting |
party |
Party/club mode | Bass boost + slight distortion |
cinema |
Movie theater | Surround-like effect + bass |
vocalBoost |
Enhance vocals | Mid-range EQ boost |
echo |
Echo/reverb | Tremolo-based echo simulation |
earrape |
Extreme distortion | High gain + distortion (1-10 intensity) |
Usage:
// Enable effects
player.effects.lofi(true);
player.effects.underwater(true);
player.effects.telephone(true);
player.effects.earrape(true, 5); // intensity 1-10
// Check active effects
console.log(player.effects.active); // ['lofi', 'underwater']
// Clear all effects
await player.effects.clear();When disabled: All player.effects methods throw Error: Feature "effects" is disabled
Voice manipulation presets for fun audio transformations.
How it works:
- Uses timescale filter to change pitch and speed
- Combines with tremolo/vibrato for complex voice effects
- Presets are optimized for voice-like audio
- Can be used on any audio, not just voice
Available voice presets:
| Preset | Description | Settings |
|---|---|---|
maleVoice |
Deeper male voice | Pitch: 0.75 |
femaleVoice |
Higher female voice | Pitch: 1.3 |
childVoice |
Child-like voice | Pitch: 1.5 |
robotVoice |
Robotic sound | Pitch: 0.9 + Tremolo |
demonVoice |
Deep demonic voice | Speed: 0.8, Pitch: 0.5 + Tremolo |
heliumVoice |
Helium balloon voice | Speed: 1.1, Pitch: 1.8 |
giantVoice |
Giant/monster voice | Speed: 0.85, Pitch: 0.6 |
whisper |
Whisper effect | Low pass + reduced volume |
Usage:
// Apply voice presets
await player.voice.robotVoice();
await player.voice.demonVoice();
await player.voice.heliumVoice();
// Custom speed/pitch
await player.voice.setSpeed(1.5); // 0.25 - 3.0
await player.voice.setPitch(0.8); // 0.25 - 3.0
await player.voice.setSpeedAndPitch(1.2, 0.9);
// Echo effect
await player.voice.echo(7); // intensity 1-10
// Reset to normal
await player.voice.reset();
// Get current settings
console.log(player.voice.getSettings());
// { speed: 1.0, pitch: 1.0, filters: {...} }When disabled: All player.voice methods throw Error: Feature "voiceEffects" is disabled
Convert text to speech and play in voice channel.
How it works:
- Generates TTS audio URL using Google Translate TTS API
- Resolves URL through Lavalink as a playable track
- Supports 28 languages and multiple speed presets
- Caches generated audio for repeated phrases
- Can interrupt, queue, or prioritize TTS messages
Supported languages:
en (English), es (Spanish), fr (French), de (German), it (Italian), pt (Portuguese), ru (Russian), ja (Japanese), ko (Korean), zh (Chinese), ar (Arabic), hi (Hindi), nl (Dutch), pl (Polish), tr (Turkish), vi (Vietnamese), th (Thai), id (Indonesian), sv (Swedish), da (Danish), no (Norwegian), fi (Finnish), cs (Czech), el (Greek), he (Hebrew), ro (Romanian), hu (Hungarian), uk (Ukrainian)
Speed presets:
| Preset | Speed |
|---|---|
verySlow |
0.5x |
slow |
0.75x |
normal |
1.0x |
fast |
1.25x |
veryFast |
1.5x |
ultraFast |
2.0x |
Usage:
const { TTS } = require('pappify');
const tts = new TTS(pappify, { lang: 'en', speed: 1.0 });
// Speak immediately (interrupts current track)
await tts.speak(player, 'Hello everyone!');
// Queue TTS (plays after current track)
await tts.queue(player, 'This will play later');
// Priority announcement (plays next)
await tts.announce(player, 'Important message!');
// With options
await tts.speak(player, 'Bonjour!', {
lang: 'fr',
speed: 1.2,
requester: user,
});
// Get available languages
console.log(tts.getLanguages());
// Clear cache
tts.clearCache();When disabled: All TTS methods throw Error: TTS feature is disabled
Fetch song lyrics from Lavalink lyrics plugins.
How it works:
- Requires lavalyrics-plugin or similar on Lavalink server
- Fetches lyrics via REST API
- Supports synced (timed) and plain lyrics
- Can search lyrics by query or get for current track
Usage:
// Check if lyrics plugin is available
const available = await player.node.lyrics.checkAvailable();
// Get lyrics for current track
const lyrics = await player.node.lyrics.getCurrentTrack(player.guildId);
console.log(lyrics.text);
console.log(lyrics.lines); // Synced lyrics with timestamps
// Get lyrics for specific track
const lyrics = await player.node.lyrics.get(track);
// Search lyrics
const results = await player.node.lyrics.search('never gonna give you up');When disabled: All node.lyrics methods throw Error: Feature "lyrics" is disabled
Mix multiple audio tracks simultaneously (NodeLink only).
How it works:
- Requires NodeLink server (not standard Lavalink)
- Allows layering multiple audio tracks
- Each layer has independent volume control
- Useful for background music, sound effects, etc.
Usage:
// Check if mixer is available
if (player.node.mixer.isAvailable()) {
// Add background music layer
await player.node.mixer.addLayer(guildId, {
track: { encoded: backgroundTrack.encoded },
volume: 0.3, // 30% volume
});
// Get active layers
const layers = await player.node.mixer.getLayers(guildId);
// Update layer volume
await player.node.mixer.updateLayerVolume(guildId, layerId, 0.5);
// Remove specific layer
await player.node.mixer.removeLayer(guildId, layerId);
// Clear all layers
await player.node.mixer.clearLayers(guildId);
}When disabled: All node.mixer methods throw Error: Feature "mixer" is disabled
Record voice channel audio (NodeLink only).
How it works:
- Requires NodeLink server (not standard Lavalink)
- Records all audio in voice channel
- Supports multiple formats (mp3, wav, ogg)
- Can record separate tracks per user or mixed
Usage:
// Start recording
await player.node.recorder.start(guildId, {
format: 'mp3',
bitrate: 128,
separateTracks: false, // true = separate file per user
});
// Check recording status
const status = await player.node.recorder.getStatus(guildId);
console.log(player.node.recorder.isRecording(guildId)); // true/false
console.log(player.node.recorder.getDuration(guildId)); // duration in ms
// Stop recording and get result
const result = await player.node.recorder.stop(guildId);
console.log(result.url); // URL to download recordingWhen disabled: All node.recorder methods throw Error: Feature "recorder" is disabled
Resolve Spotify URLs to playable tracks.
How it works:
- Extracts track/album/playlist info from Spotify API
- Searches for equivalent tracks on YouTube/other sources
- Supports tracks, albums, playlists, and artist top tracks
- Requires Spotify API credentials for full functionality
Supported URL types:
https://open.spotify.com/track/...- Single trackhttps://open.spotify.com/album/...- Full albumhttps://open.spotify.com/playlist/...- Playlisthttps://open.spotify.com/artist/...- Artist top tracks
Usage with SpotifyPlugin:
const { SpotifyPlugin } = require('pappify');
const spotify = new SpotifyPlugin({
clientId: 'your_spotify_client_id',
clientSecret: 'your_spotify_client_secret',
market: 'US',
playlistLimit: 100,
albumLimit: 50,
});
const pappify = new Pappify(client, nodes, {
plugins: [spotify],
spotify: true, // Enable Spotify feature
});
// Now Spotify URLs work automatically
const result = await pappify.resolve({
query: 'https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT',
});Standalone Spotify class:
const { Spotify } = require('pappify');
const spotify = new Spotify(pappify, {
clientId: 'xxx',
clientSecret: 'xxx',
});
// Check URL type
spotify.isSpotifyUrl(url); // true/false
spotify.getType(url); // 'track', 'album', 'playlist', 'artist'
spotify.extractId(url); // 'trackId123'
// Get Spotify data
const track = await spotify.getTrack(id);
const album = await spotify.getAlbum(id);
const playlist = await spotify.getPlaylist(id);
// Resolve to playable tracks
const result = await spotify.resolve(url, requester);When disabled: Spotify URLs won't auto-resolve, SpotifyPlugin won't load tracks
const pappify = new Pappify(client, nodes, {
send: (data) => client.guilds.cache.get(data.d.guild_id)?.shard.send(data),
// Disable features you don't need
autoplay: false, // Disable autoplay
tts: false, // Disable TTS
mixer: false, // Disable mixer
recorder: false, // Disable recorder
// Keep these enabled (default)
filters: true,
effects: true,
voiceEffects: true,
lyrics: true,
spotify: true,
});| Method | Returns | Description |
|---|---|---|
isEnabled(feature) |
boolean | Check if a feature is enabled |
enable(feature) |
Pappify | Enable a feature at runtime |
disable(feature) |
Pappify | Disable a feature at runtime |
getFeatures() |
Object | Get all feature states |
setConfig(config) |
Pappify | Update configuration |
getConfig() |
Object | Get current configuration |
// Check if feature is enabled
if (pappify.isEnabled('tts')) {
await tts.speak(player, 'Hello!');
}
// Enable/disable at runtime
pappify.enable('autoplay');
pappify.disable('mixer');
// Get all feature states
console.log(pappify.getFeatures());
// { autoplay: true, filters: true, effects: true, ... }
// Update multiple settings
pappify.setConfig({
tts: true,
autoplay: false,
defaultVolume: 80,
});| Feature | When Disabled |
|---|---|
autoplay |
player.autoplay() throws error |
filters |
player.filters methods throw error |
effects |
player.effects methods throw error |
voiceEffects |
player.voice methods throw error |
tts |
tts.play(), tts.speak() throw error |
lyrics |
node.lyrics methods throw error |
mixer |
node.mixer methods throw error |
recorder |
node.recorder methods throw error |
spotify |
Spotify URLs won't auto-resolve |
try {
await player.autoplay(player);
} catch (error) {
if (error.message.includes('disabled')) {
console.log('Autoplay is disabled in config');
}
}
// Or check before using
if (pappify.isEnabled('autoplay')) {
await player.autoplay(player);
} else {
console.log('Autoplay is not available');
}| Property | Type | Description |
|---|---|---|
client |
Object | Discord client |
nodes |
Array | Node configurations |
nodeMap |
Map | Map of connected nodes |
players |
Map | Map of active players |
clientId |
string | Bot user ID |
initiated |
boolean | Whether initialized |
version |
string | Pappify version |
leastUsedNodes |
Node[] | Nodes sorted by usage |
bestNode |
Node | Best node by penalties |
| Method | Returns | Description |
|---|---|---|
init(clientId) |
Pappify | Initialize with bot ID |
createNode(options) |
Node | Create and connect a node |
destroyNode(identifier) |
void | Destroy a node |
createConnection(options) |
Player | Create a player connection |
createPlayer(node, options) |
Player | Create player on specific node |
destroyPlayer(guildId) |
void | Destroy a player |
removeConnection(guildId) |
void | Remove player connection |
get(guildId) |
Player | Get player by guild ID |
updateVoiceState(packet) |
void | Handle voice state updates |
fetchRegion(region) |
Node[] | Get nodes by region |
migrate(target, node?) |
Promise | Migrate player(s) to node |
resolve(options) |
Promise | Search/resolve tracks |
decodeTrack(track, node?) |
Promise | Decode a track |
decodeTracks(tracks, node?) |
Promise | Decode multiple tracks |
isEnabled(feature) |
boolean | Check if feature is enabled |
enable(feature) |
Pappify | Enable a feature |
disable(feature) |
Pappify | Disable a feature |
getFeatures() |
Object | Get all feature states |
setConfig(config) |
Pappify | Update configuration |
getConfig() |
Object | Get current configuration |
await pappify.resolve({
query: 'search term or URL',
source: 'ytmsearch', // optional
requester: user, // optional
node: 'nodeName', // optional
});Returns:
{
loadType: 'track' | 'playlist' | 'search' | 'empty' | 'error',
tracks: Track[],
playlistInfo: { name: string } | null,
pluginInfo: Object,
exception: Object | null,
}Represents a Lavalink node connection.
// Created internally by Pappify
pappify.createNode({
name: 'main',
host: 'localhost',
port: 2333,
password: 'youshallnotpass',
secure: false,
regions: ['us', 'eu'],
sessionId: null,
});| Property | Type | Description |
|---|---|---|
name |
string | Node identifier |
host |
string | Lavalink host |
port |
number | Lavalink port |
password |
string | Lavalink password |
secure |
boolean | Use HTTPS/WSS |
regions |
string[] | Supported regions |
sessionId |
string | Current session ID |
connected |
boolean | Connection status |
info |
Object | Node info from Lavalink |
stats |
Object | Node statistics |
penalties |
number | Load balancing score |
isNodeLink |
boolean | Is NodeLink server |
rest |
Rest | REST client |
lyrics |
Lyrics | Lyrics manager |
mixer |
Mixer | Audio mixer (NodeLink) |
recorder |
VoiceRecorder | Voice recorder (NodeLink) |
{
players: 0,
playingPlayers: 0,
uptime: 0,
memory: { free: 0, used: 0, allocated: 0, reservable: 0 },
cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0 },
frameStats: { sent: 0, nulled: 0, deficit: 0 },
}| Method | Returns | Description |
|---|---|---|
connect() |
Promise | Connect to node |
disconnect() |
void | Disconnect from node |
destroy(clean?) |
void | Destroy node |
fetchInfo(options?) |
Promise | Fetch node info |
Represents a guild audio player.
// Created via pappify.createConnection()
const player = pappify.createConnection({
guildId: '123456789',
voiceChannel: '987654321',
textChannel: '111222333',
deaf: true,
mute: false,
defaultVolume: 80,
loop: 'none',
});| Property | Type | Description |
|---|---|---|
pappify |
Pappify | Pappify instance |
node |
Node | Current node |
guildId |
string | Guild ID |
voiceChannel |
string | Voice channel ID |
textChannel |
string | Text channel ID |
connection |
Connection | Voice connection |
queue |
Queue | Track queue |
filters |
Filters | Audio filters |
effects |
Effects | Audio effects |
voice |
Voice | Voice utilities |
current |
Track | Currently playing track |
previous |
Track | Previous track |
previousTracks |
Track[] | Track history |
volume |
number | Current volume (0-1000) |
loop |
string | Loop mode: 'none', 'track', 'queue' |
position |
number | Current position in ms |
timestamp |
number | Last update timestamp |
ping |
number | Voice connection ping |
playing |
boolean | Is playing |
paused |
boolean | Is paused |
connected |
boolean | Is connected |
isAutoplay |
boolean | Autoplay enabled |
deaf |
boolean | Self deafened |
mute |
boolean | Self muted |
data |
Object | Custom data storage |
| Method | Returns | Description |
|---|---|---|
connect(options?) |
Player | Connect to voice |
disconnect() |
Player | Disconnect from voice |
play() |
Promise<Player> | Start playing |
stop() |
Player | Stop playback |
pause(paused?) |
Player | Pause/resume |
seek(position) |
Player | Seek to position (ms) |
setVolume(volume) |
Player | Set volume (0-1000) |
setLoop(mode) |
Player | Set loop mode |
setTextChannel(id) |
Player | Set text channel |
setVoiceChannel(id, opts?) |
Player | Move to channel |
skip() |
Promise<Player> | Skip current track |
autoplay(player|false) |
Promise<Player> | Enable/disable autoplay |
destroy() |
void | Destroy player |
moveTo(node) |
Promise<Player> | Move to different node |
restart() |
Promise<Player> | Restart current track |
set(key, value) |
any | Set custom data |
get(key) |
any | Get custom data |
clearData() |
Player | Clear custom data |
Extended Array for track queue management.
| Property | Type | Description |
|---|---|---|
size |
number | Queue length |
first |
Track | First track |
last |
Track | Last track |
isEmpty |
boolean | Is queue empty |
totalDuration |
number | Total duration in ms |
formattedDuration |
string | Duration as H:MM:SS |
| Method | Returns | Description |
|---|---|---|
add(track) |
Queue | Add track(s) |
remove(index) |
Track | Remove at index |
removeRange(start, count) |
Track[] | Remove range |
clear() |
void | Clear queue |
shuffle() |
Queue | Shuffle queue |
move(from, to) |
Queue | Move track |
swap(i1, i2) |
Queue | Swap two tracks |
reverse() |
Queue | Reverse order |
peek(start?, end?) |
Track[] | Get slice |
skipTo(index) |
Track[] | Skip to index |
getByRequester(user) |
Track[] | Get by requester |
removeByRequester(user) |
Track[] | Remove by requester |
removeDuplicates() |
Queue | Remove duplicates |
// Add tracks
player.queue.add(track);
player.queue.add([track1, track2, track3]);
// Manipulate
player.queue.shuffle();
player.queue.move(0, 5);
player.queue.swap(1, 3);
player.queue.remove(2);
player.queue.removeDuplicates();
// Info
console.log(player.queue.size);
console.log(player.queue.formattedDuration); // "1:23:45"Represents a playable track.
| Property | Type | Description |
|---|---|---|
encoded |
string | Encoded track string |
track |
string | Alias for encoded |
info |
Object | Track information |
pluginInfo |
Object | Plugin data |
{
identifier: 'dQw4w9WgXcQ',
title: 'Never Gonna Give You Up',
author: 'Rick Astley',
length: 213000,
uri: 'https://youtube.com/watch?v=dQw4w9WgXcQ',
sourceName: 'youtube',
artworkUrl: 'https://...',
isrc: 'GBARL9300135',
seekable: true,
stream: false,
position: 0,
requester: User,
}| Method | Returns | Description |
|---|---|---|
getThumbnail() |
Promise<string> | Get thumbnail URL |
resolve(pappify) |
Promise<Track> | Resolve unresolved track |
toJSON() |
Object | Serialize track |
Audio filter presets and controls.
| Property | Type | Description |
|---|---|---|
volume |
number | Filter volume (0-5) |
equalizer |
Array | EQ bands |
karaoke |
Object | Karaoke settings |
timescale |
Object | Speed/pitch/rate |
tremolo |
Object | Tremolo settings |
vibrato |
Object | Vibrato settings |
rotation |
Object | 8D rotation |
distortion |
Object | Distortion settings |
channelMix |
Object | Channel mixing |
lowPass |
Object | Low pass filter |
bassboost |
number | Bassboost level (0-10) |
nightcore |
boolean | Nightcore enabled |
vaporwave |
boolean | Vaporwave enabled |
slowmode |
boolean | Slowmode enabled |
_8d |
boolean | 8D audio enabled |
daycore |
boolean | Daycore enabled |
chipmunk |
boolean | Chipmunk enabled |
darthvader |
boolean | Darth Vader enabled |
doubletime |
boolean | Doubletime enabled |
pop |
boolean | Pop preset |
soft |
boolean | Soft preset |
trebleBass |
boolean | Treble bass preset |
| Method | Returns | Description |
|---|---|---|
setEqualizer(bands) |
Filters | Set EQ bands |
setKaraoke(enabled, opts?) |
Filters | Toggle karaoke |
setTimescale(enabled, opts?) |
Filters | Set timescale |
setTremolo(enabled, opts?) |
Filters | Toggle tremolo |
setVibrato(enabled, opts?) |
Filters | Toggle vibrato |
setRotation(enabled, opts?) |
Filters | Toggle rotation |
setDistortion(enabled, opts?) |
Filters | Toggle distortion |
setChannelMix(enabled, opts?) |
Filters | Set channel mix |
setLowPass(enabled, opts?) |
Filters | Toggle low pass |
setBassboost(enabled, opts?) |
Filters | Set bassboost |
setNightcore(enabled, opts?) |
Filters | Toggle nightcore |
setVaporwave(enabled, opts?) |
Filters | Toggle vaporwave |
setSlowmode(enabled, opts?) |
Filters | Toggle slowmode |
set8D(enabled, opts?) |
Filters | Toggle 8D |
setDaycore(enabled, opts?) |
Filters | Toggle daycore |
setChipmunk(enabled) |
Filters | Toggle chipmunk |
setDarthvader(enabled) |
Filters | Toggle darth vader |
setDoubletime(enabled) |
Filters | Toggle doubletime |
setPop(enabled) |
Filters | Toggle pop preset |
setSoft(enabled) |
Filters | Toggle soft preset |
setTrebleBass(enabled) |
Filters | Toggle treble bass |
clearFilters() |
Promise<Filters> | Clear all filters |
toJSON() |
Object | Get filter state |
// Presets
player.filters.setBassboost(true, { value: 8 });
player.filters.setNightcore(true);
player.filters.set8D(true);
// Custom EQ
player.filters.setEqualizer([
{ band: 0, gain: 0.5 },
{ band: 1, gain: 0.3 },
]);
// Custom timescale
player.filters.setTimescale(true, {
speed: 1.2,
pitch: 1.1,
rate: 1.0,
});
// Clear all
await player.filters.clearFilters();Advanced audio effects beyond basic filters.
| Property | Type | Description |
|---|---|---|
active |
string[] | List of active effects |
| Method | Returns | Description |
|---|---|---|
lofi(enabled) |
Effects | Lo-fi effect |
underwater(enabled) |
Effects | Underwater effect |
telephone(enabled) |
Effects | Telephone effect |
radio(enabled) |
Effects | Radio effect |
party(enabled) |
Effects | Party mode |
cinema(enabled) |
Effects | Cinema effect |
vocalBoost(enabled) |
Effects | Vocal boost |
echo(enabled, opts?) |
Effects | Echo effect |
earrape(enabled, intensity?) |
Effects | Earrape (1-10) |
clear() |
Promise<Effects> | Clear all effects |
// Enable effects
player.effects.lofi(true);
player.effects.underwater(true);
player.effects.earrape(true, 5);
// Check active
console.log(player.effects.active); // ['lofi', 'underwater', 'earrape']
// Clear all
await player.effects.clear();Voice manipulation utilities.
| Property | Type | Description |
|---|---|---|
_speed |
number | Current speed |
_pitchShift |
number | Current pitch |
| Method | Returns | Description |
|---|---|---|
setSpeed(speed) |
Promise<Voice> | Set speed (0.25-3.0) |
setPitch(pitch) |
Promise<Voice> | Set pitch (0.25-3.0) |
setSpeedAndPitch(s, p) |
Promise<Voice> | Set both |
reset() |
Promise<Voice> | Reset to normal |
maleVoice() |
Promise<Voice> | Male voice preset |
femaleVoice() |
Promise<Voice> | Female voice preset |
childVoice() |
Promise<Voice> | Child voice preset |
robotVoice() |
Promise<Voice> | Robot voice preset |
demonVoice() |
Promise<Voice> | Demon voice preset |
heliumVoice() |
Promise<Voice> | Helium voice preset |
giantVoice() |
Promise<Voice> | Giant voice preset |
whisper() |
Promise<Voice> | Whisper effect |
echo(intensity?) |
Promise<Voice> | Echo effect (1-10) |
getSettings() |
Object | Get current settings |
// Speed/pitch
await player.voice.setSpeed(1.5);
await player.voice.setPitch(0.8);
// Voice presets
await player.voice.robotVoice();
await player.voice.demonVoice();
await player.voice.heliumVoice();
// Reset
await player.voice.reset();Text-to-Speech manager.
const { TTS } = require('pappify');
const tts = new TTS(pappify, {
lang: 'en',
speed: 1.0,
provider: 'google',
maxCacheSize: 100,
});TTS.LANGUAGES = {
en: 'English', es: 'Spanish', fr: 'French', de: 'German',
it: 'Italian', pt: 'Portuguese', ru: 'Russian', ja: 'Japanese',
ko: 'Korean', zh: 'Chinese', ar: 'Arabic', hi: 'Hindi',
nl: 'Dutch', pl: 'Polish', tr: 'Turkish', vi: 'Vietnamese',
th: 'Thai', id: 'Indonesian', sv: 'Swedish', da: 'Danish',
no: 'Norwegian', fi: 'Finnish', cs: 'Czech', el: 'Greek',
he: 'Hebrew', ro: 'Romanian', hu: 'Hungarian', uk: 'Ukrainian',
};
TTS.SPEEDS = {
verySlow: 0.5, slow: 0.75, normal: 1.0,
fast: 1.25, veryFast: 1.5, ultraFast: 2.0,
};| Method | Returns | Description |
|---|---|---|
play(player, text, opts?) |
Promise<Track> | Play TTS |
speak(player, text, opts?) |
Promise<Track> | Speak immediately (interrupts) |
queue(player, text, opts?) |
Promise<Track> | Queue TTS |
announce(player, text, opts?) |
Promise<Track> | Priority announcement |
generateUrl(text, opts?) |
string | Generate TTS URL |
clearCache() |
void | Clear TTS cache |
getLanguages() |
Object | Get available languages |
getSpeeds() |
Object | Get speed presets |
// Speak immediately
await tts.speak(player, 'Hello world!');
// Queue TTS
await tts.queue(player, 'This will play after current track');
// Priority announcement
await tts.announce(player, 'Important message!');
// With options
await tts.speak(player, 'Bonjour!', {
lang: 'fr',
speed: 1.2,
requester: user,
});HTTP client for Lavalink REST API.
| Property | Type | Description |
|---|---|---|
url |
string | Base URL |
version |
string | REST version |
sessionId |
string | Session ID |
calls |
number | Total API calls |
| Method | Returns | Description |
|---|---|---|
makeRequest(method, endpoint, body?, headers?) |
Promise | Make HTTP request |
getPlayers() |
Promise | Get all players |
getPlayer(guildId) |
Promise | Get player |
updatePlayer(options) |
Promise | Update player |
destroyPlayer(guildId) |
Promise | Destroy player |
loadTracks(identifier) |
Promise | Load tracks |
decodeTrack(track) |
Promise | Decode track |
decodeTracks(tracks) |
Promise | Decode tracks |
getStats() |
Promise | Get node stats |
getInfo() |
Promise | Get node info |
getVersion() |
Promise | Get version |
updateSession(resuming, timeout) |
Promise | Update session |
getRoutePlannerStatus() |
Promise | Get route planner |
freeRoutePlannerAddress(address) |
Promise | Free address |
freeAllRoutePlannerAddresses() |
Promise | Free all addresses |
Lyrics fetching (requires lavalyrics-plugin).
| Method | Returns | Description |
|---|---|---|
checkAvailable(eitherOne?, ...plugins) |
Promise<boolean> | Check plugin availability |
get(track, skipSource?) |
Promise<Object> | Get lyrics for track |
getCurrentTrack(guildId, skipSource?, plugin?) |
Promise<Object> | Get current track lyrics |
search(query) |
Promise<Object> | Search lyrics |
// Get lyrics for current track
const lyrics = await player.node.lyrics.getCurrentTrack(player.guildId);
console.log(lyrics.text);
// Get lyrics for specific track
const lyrics = await player.node.lyrics.get(track);
// Search lyrics
const results = await player.node.lyrics.search('never gonna give you up');Audio mixing for NodeLink servers.
| Method | Returns | Description |
|---|---|---|
isAvailable() |
boolean | Check if available |
addLayer(guildId, options) |
Promise | Add mix layer |
getLayers(guildId) |
Promise | Get active layers |
updateLayerVolume(guildId, mixId, volume) |
Promise | Update layer volume |
removeLayer(guildId, mixId) |
Promise | Remove layer |
clearLayers(guildId) |
Promise | Remove all layers |
// Add background music layer
await player.node.mixer.addLayer(guildId, {
track: { encoded: backgroundTrack.encoded },
volume: 0.3,
});
// Get layers
const layers = await player.node.mixer.getLayers(guildId);
// Update volume
await player.node.mixer.updateLayerVolume(guildId, layerId, 0.5);
// Remove
await player.node.mixer.removeLayer(guildId, layerId);Auto-resolve Spotify URLs to playable tracks.
const { SpotifyPlugin } = require('pappify');
const spotify = new SpotifyPlugin({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
market: 'US',
playlistLimit: 100,
albumLimit: 50,
});
const pappify = new Pappify(client, nodes, {
plugins: [spotify],
// ...
});
// Now Spotify URLs work automatically
const result = await pappify.resolve({
query: 'https://open.spotify.com/track/...',
});Auto-save and restore player states.
const { SaveStatePlugin } = require('pappify');
const saveState = new SaveStatePlugin({
path: './states.json',
saveInterval: 30000,
autoRestore: true,
});
const pappify = new Pappify(client, nodes, {
plugins: [saveState],
});
// Manual save/restore
await pappify.saveStates();
await pappify.restoreStates();Auto-disconnect on idle or empty channel.
const { AutoDisconnectPlugin } = require('pappify');
const autoDisconnect = new AutoDisconnectPlugin({
timeout: 300000, // 5 min idle timeout
leaveOnEmpty: true, // Leave when channel empty
emptyDelay: 30000, // 30s delay before leaving
leaveOnQueueEnd: false, // Leave when queue ends
queueEndDelay: 60000, // 1 min delay after queue end
});
const pappify = new Pappify(client, nodes, {
plugins: [autoDisconnect],
});const { Plugin } = require('pappify');
class MyPlugin extends Plugin {
constructor(options) {
super('MyPlugin');
this.options = options;
}
load(pappify) {
// Called when plugin loads
pappify.on('trackStart', (player, track) => {
console.log(`Playing: ${track.info.title}`);
});
pappify.emit('debug', '[MyPlugin] Loaded');
}
unload(pappify) {
// Called when plugin unloads
pappify.emit('debug', '[MyPlugin] Unloaded');
}
}
module.exports = { MyPlugin };| Event | Parameters | Description |
|---|---|---|
nodeConnect |
node |
Node connected |
nodeDisconnect |
node, { code, reason } |
Node disconnected |
nodeError |
node, error |
Node error |
nodeReconnect |
node |
Node reconnecting |
nodeCreate |
node |
Node created |
nodeDestroy |
node |
Node destroyed |
playerCreate |
player |
Player created |
playerDestroy |
player |
Player destroyed |
playerDisconnect |
player |
Player disconnected |
playerMove |
oldChannel, newChannel |
Bot moved channels |
playerMoved |
player, oldNode, newNode |
Player migrated |
playerMigrated |
player, oldNode, newNode |
Migration success |
playerMigrationFailed |
player, error |
Migration failed |
nodeMigrated |
node, players[] |
All players migrated |
nodeMigrationFailed |
node, error |
Node migration failed |
trackStart |
player, track, payload |
Track started |
trackEnd |
player, track, payload |
Track ended |
trackError |
player, track, payload |
Track error |
trackStuck |
player, track, payload |
Track stuck |
queueEnd |
player |
Queue finished |
socketClosed |
player, payload |
WebSocket closed |
playerUpdate |
player, packet |
Player state update |
raw |
type, payload |
Raw Lavalink data |
debug |
message |
Debug message |
apiResponse |
endpoint, response |
REST API response |
// Node events
pappify.on('nodeConnect', (node) => {
console.log(`Node ${node.name} connected`);
});
pappify.on('nodeError', (node, error) => {
console.error(`Node ${node.name} error:`, error);
});
// Player events
pappify.on('playerCreate', (player) => {
console.log(`Player created for ${player.guildId}`);
});
// Track events
pappify.on('trackStart', (player, track) => {
const channel = client.channels.cache.get(player.textChannel);
channel?.send(`Now playing: ${track.info.title}`);
});
pappify.on('trackEnd', (player, track, payload) => {
console.log(`Track ended: ${track.info.title} (${payload.reason})`);
});
pappify.on('trackError', (player, track, payload) => {
console.error(`Track error:`, payload.exception);
});
pappify.on('queueEnd', (player) => {
const channel = client.channels.cache.get(player.textChannel);
channel?.send('Queue finished!');
});
// Debug
pappify.on('debug', (message) => {
console.log(`[Pappify] ${message}`);
});
// Migration
pappify.on('playerMigrated', (player, oldNode, newNode) => {
console.log(`Player migrated from ${oldNode.name} to ${newNode.name}`);
});| Reason | Description |
|---|---|
finished |
Track finished normally |
loadFailed |
Track failed to load |
stopped |
Track was stopped |
replaced |
Track was replaced |
cleanup |
Player was destroyed |
Pappify includes TypeScript definitions.
import {
Pappify,
Node,
Player,
Track,
Queue,
Filters,
Effects,
Voice,
TTS,
Plugin,
SpotifyPlugin,
SaveStatePlugin,
AutoDisconnectPlugin,
} from 'pappify';
// Types are automatically inferred
const pappify = new Pappify(client, nodes, options);
pappify.on('trackStart', (player: Player, track: Track) => {
console.log(track.info.title);
});Save and restore player states.
const { PlayerState } = require('pappify');
// Serialize player
const state = PlayerState.serialize(player);
// Serialize all players
const states = PlayerState.serializeAll(pappify);
// Restore player
const player = await PlayerState.restore(pappify, state, {
resume: true,
});
// Restore all
const players = await PlayerState.restoreAll(pappify, states);Record voice channel audio.
// Start recording
await player.node.recorder.start(guildId, {
format: 'mp3',
bitrate: 128,
separateTracks: false,
});
// Check status
const status = await player.node.recorder.getStatus(guildId);
console.log(player.node.recorder.isRecording(guildId));
console.log(player.node.recorder.getDuration(guildId));
// Stop recording
const result = await player.node.recorder.stop(guildId);Use Spotify API directly.
const { Spotify } = require('pappify');
const spotify = new Spotify(pappify, {
clientId: 'xxx',
clientSecret: 'xxx',
});
// Check URL type
spotify.isSpotifyUrl(url); // true/false
spotify.getType(url); // 'track', 'album', 'playlist', 'artist'
spotify.extractId(url); // 'trackId123'
// Get data
const track = await spotify.getTrack(id);
const album = await spotify.getAlbum(id);
const playlist = await spotify.getPlaylist(id);
const artist = await spotify.getArtist(id);
// Resolve to playable tracks
const result = await spotify.resolve(url, requester);| Platform | Prefix | Description |
|---|---|---|
| YouTube Music | ytmsearch: |
YouTube Music search |
| YouTube | ytsearch: |
YouTube search |
| SoundCloud | scsearch: |
SoundCloud search |
| Spotify | spsearch: |
Spotify search (requires plugin) |
| Deezer | dzsearch: |
Deezer search (requires plugin) |
| Apple Music | amsearch: |
Apple Music (requires plugin) |
// Search examples
await pappify.resolve({ query: 'never gonna give you up' }); // Uses default
await pappify.resolve({ query: 'ytsearch:rick astley' });
await pappify.resolve({ query: 'scsearch:electronic music' });
// Direct URLs
await pappify.resolve({ query: 'https://youtube.com/watch?v=...' });
await pappify.resolve({ query: 'https://open.spotify.com/track/...' });
await pappify.resolve({ query: 'https://soundcloud.com/...' });pappify/
├── build/
│ ├── core/ # Main client & plugin base
│ │ ├── Pappify.js
│ │ └── Plugin.js
│ ├── network/ # Node, REST, Lavalink features
│ │ ├── Node.js
│ │ ├── Rest.js
│ │ ├── Lyrics.js
│ │ ├── Mixer.js
│ │ └── VoiceRecorder.js
│ ├── player/ # Player, queue, tracks
│ │ ├── Player.js
│ │ ├── Queue.js
│ │ ├── Track.js
│ │ ├── Connection.js
│ │ ├── PlayerState.js
│ │ └── Spotify.js
│ ├── audio/ # Filters, effects, voice, TTS
│ │ ├── Filters.js
│ │ ├── Effects.js
│ │ ├── Voice.js
│ │ └── TTS.js
│ ├── plugins/ # Built-in plugins
│ │ ├── SpotifyPlugin.js
│ │ ├── SaveStatePlugin.js
│ │ └── AutoDisconnectPlugin.js
│ ├── index.js
│ └── index.d.ts
├── examples/
│ └── basic-bot.js
├── package.json
├── README.md
└── DOCUMENTATION.md
MIT © Pappu100