magmastream
Version:
A user-friendly Lavalink client designed for NodeJS.
1,227 lines (1,221 loc) • 90 kB
TypeScript
import { User, ClientUser, Message } from 'discord.js';
import WebSocket from 'ws';
import { Collection } from '@discordjs/collection';
import { EventEmitter } from 'events';
/** Represents an equalizer band. */
interface Band {
/** The index of the equalizer band (0-12). */
band: number;
/** The gain value of the equalizer band (in decibels). */
gain: number;
}
/** Handles the requests sent to the Lavalink REST API. */
declare class Rest {
/** The Node that this Rest instance is connected to. */
private node;
/** The ID of the current session. */
private sessionId;
/** The password for the Node. */
private readonly password;
/** The URL of the Node. */
private readonly url;
/** The Manager instance. */
manager: Manager;
constructor(node: Node, manager: Manager);
/**
* Sets the session ID.
* This method is used to set the session ID after a resume operation is done.
* @param {string} sessionId The session ID to set.
* @returns {string} Returns the set session ID.
*/
setSessionId(sessionId: string): string;
/**
* Retrieves all the players that are currently running on the node.
* @returns {Promise<unknown>} Returns the result of the GET request.
*/
getAllPlayers(): Promise<unknown>;
/**
* Sends a PATCH request to update player related data.
* @param {playOptions} options The options to update the player with.
* @returns {Promise<unknown>} Returns the result of the PATCH request.
*/
updatePlayer(options: playOptions): Promise<unknown>;
/**
* Sends a DELETE request to the server to destroy the player.
* @param {string} guildId The guild ID of the player to destroy.
* @returns {Promise<unknown>} Returns the result of the DELETE request.
*/
destroyPlayer(guildId: string): Promise<unknown>;
/**
* Updates the session status for resuming.
* This method sends a PATCH request to update the session's resuming status and timeout.
*
* @param {boolean} resuming - Indicates whether the session should be set to resuming.
* @param {number} timeout - The timeout duration for the session resume.
* @returns {Promise<unknown>} The result of the PATCH request.
*/
updateSession(resuming: boolean, timeout: number): Promise<unknown>;
/**
* Sends a request to the specified endpoint and returns the response data.
* @param {string} method The HTTP method to use for the request.
* @param {string} endpoint The endpoint to send the request to.
* @param {unknown} [body] The data to send in the request body.
* @returns {Promise<unknown>} The response data of the request.
*/
private request;
/**
* Sends a GET request to the specified endpoint and returns the response data.
* @param {string} endpoint The endpoint to send the GET request to.
* @returns {Promise<unknown>} The response data of the GET request.
*/
get(endpoint: string): Promise<unknown>;
/**
* Sends a PATCH request to the specified endpoint and returns the response data.
* @param {string} endpoint The endpoint to send the PATCH request to.
* @param {unknown} body The data to send in the request body.
* @returns {Promise<unknown>} The response data of the PATCH request.
*/
patch(endpoint: string, body: unknown): Promise<unknown>;
/**
* Sends a POST request to the specified endpoint and returns the response data.
* @param {string} endpoint The endpoint to send the POST request to.
* @param {unknown} body The data to send in the request body.
* @returns {Promise<unknown>} The response data of the POST request.
*/
post(endpoint: string, body: unknown): Promise<unknown>;
/**
* Sends a PUT request to the specified endpoint and returns the response data.
* @param {string} endpoint The endpoint to send the PUT request to.
* @param {unknown} body The data to send in the request body.
* @returns {Promise<unknown>} The response data of the PUT request.
*/
put(endpoint: string, body: unknown): Promise<unknown>;
/**
* Sends a DELETE request to the specified endpoint.
* @param {string} endpoint - The endpoint to send the DELETE request to.
* @returns {Promise<unknown>} The response data of the DELETE request.
*/
delete(endpoint: string): Promise<unknown>;
}
interface playOptions {
guildId: string;
data: {
/** The base64 encoded track. */
encodedTrack?: string;
/** The track ID. */
identifier?: string;
/** The track time to start at. */
startTime?: number;
/** The track time to end at. */
endTime?: number;
/** The player volume level. */
volume?: number;
/** The player position in a track. */
position?: number;
/** Whether the player is paused. */
paused?: boolean;
/** The audio effects. */
filters?: object;
/** voice payload. */
voice?: {
token: string;
sessionId: string;
endpoint: string;
};
/** Whether to not replace the track if a play payload is sent. */
noReplace?: boolean;
};
}
declare enum SponsorBlockSegment {
Sponsor = "sponsor",
SelfPromo = "selfpromo",
Interaction = "interaction",
Intro = "intro",
Outro = "outro",
Preview = "preview",
MusicOfftopic = "music_offtopic",
Filler = "filler"
}
declare class Node {
options: NodeOptions;
/** The socket for the node. */
socket: WebSocket | null;
/** The stats for the node. */
stats: NodeStats;
/** The manager for the node */
manager: Manager;
/** The node's session ID. */
sessionId: string | null;
/** The REST instance. */
readonly rest: Rest;
/** Actual Lavalink information of the node. */
info: LavalinkInfo | null;
private static _manager;
private reconnectTimeout?;
private reconnectAttempts;
/**
* Creates an instance of Node.
* @param options
*/
constructor(options: NodeOptions);
/** Returns if connected to the Node. */
get connected(): boolean;
/** Returns the address for this node. */
get address(): string;
/** @hidden */
static init(manager: Manager): void;
/**
* Creates the sessionIds.json file if it doesn't exist. This file is used to
* store the session IDs for each node. The session IDs are used to identify
* the node when resuming a session.
*/
createSessionIdsFile(): void;
/**
* Loads session IDs from the sessionIds.json file if it exists.
* The session IDs are used to resume sessions for each node.
*
* The session IDs are stored in the sessionIds.json file as a composite key
* of the node identifier and cluster ID. This allows multiple clusters to
* be used with the same node identifier.
*/
loadSessionIds(): void;
/**
* Updates the session ID in the sessionIds.json file.
*
* This method is called after the session ID has been updated, and it
* writes the new session ID to the sessionIds.json file.
*
* @remarks
* The session ID is stored in the sessionIds.json file as a composite key
* of the node identifier and cluster ID. This allows multiple clusters to
* be used with the same node identifier.
*/
updateSessionId(): void;
/**
* Connects to the Node.
*
* @remarks
* If the node is already connected, this method will do nothing.
* If the node has a session ID, it will be sent in the headers of the WebSocket connection.
* If the node has no session ID but the `resumeStatus` option is true, it will use the session ID
* stored in the sessionIds.json file if it exists.
*/
connect(): void;
/**
* Destroys the node and cleans up associated resources.
*
* This method emits a debug event indicating that the node is being destroyed and attempts
* to automatically move all players connected to the node to a usable one. It then closes
* the WebSocket connection, removes all event listeners, and clears the reconnect timeout.
* Finally, it emits a "nodeDestroy" event and removes the node from the manager.
*
* @returns {Promise<void>} A promise that resolves when the node and its resources have been destroyed.
*/
destroy(): Promise<void>;
/**
* Attempts to reconnect to the node if the connection is lost.
*
* This method is called when the WebSocket connection is closed
* unexpectedly. It will attempt to reconnect to the node after a
* specified delay, and will continue to do so until the maximum
* number of retry attempts is reached or the node is manually destroyed.
* If the maximum number of retry attempts is reached, an error event
* will be emitted and the node will be destroyed.
*
* @returns {Promise<void>} - Resolves when the reconnection attempt is scheduled.
* @emits {debug} - Emits a debug event indicating the node is attempting to reconnect.
* @emits {nodeReconnect} - Emits a nodeReconnect event when the node is attempting to reconnect.
* @emits {nodeError} - Emits an error event if the maximum number of retry attempts is reached.
* @emits {nodeDestroy} - Emits a nodeDestroy event if the maximum number of retry attempts is reached.
*/
private reconnect;
/**
* Handles the "open" event emitted by the WebSocket connection.
*
* This method is called when the WebSocket connection is established.
* It clears any existing reconnect timeouts, emits a debug event
* indicating the node is connected, and emits a "nodeConnect" event
* with the node as the argument.
*/
protected open(): void;
/**
* Handles the "close" event emitted by the WebSocket connection.
*
* This method is called when the WebSocket connection is closed.
* It emits a "nodeDisconnect" event with the node and the close event as arguments,
* and a debug event indicating the node is disconnected.
* It then attempts to move all players connected to that node to a useable one.
* If the close event was not initiated by the user, it will also attempt to reconnect.
*
* @param {number} code The close code of the WebSocket connection.
* @param {string} reason The reason for the close event.
* @returns {Promise<void>} A promise that resolves when the disconnection is handled.
*/
protected close(code: number, reason: string): Promise<void>;
/**
* Handles the "error" event emitted by the WebSocket connection.
*
* This method is called when an error occurs on the WebSocket connection.
* It emits a "nodeError" event with the node and the error as arguments and
* a debug event indicating the error on the node.
* @param {Error} error The error that occurred.
*/
protected error(error: Error): void;
/**
* Handles incoming messages from the Lavalink WebSocket connection.
* @param {Buffer | string} d The message received from the WebSocket connection.
* @returns {Promise<void>} A promise that resolves when the message is handled.
* @emits {debug} - Emits a debug event with the message received from the WebSocket connection.
* @emits {nodeError} - Emits a nodeError event if an unexpected op is received.
* @emits {nodeRaw} - Emits a nodeRaw event with the raw message received from the WebSocket connection.
* @private
*/
protected message(d: Buffer | string): Promise<void>;
/**
* Handles an event emitted from the Lavalink node.
* @param {PlayerEvent & PlayerEvents} payload The event emitted from the node.
* @returns {Promise<void>} A promise that resolves when the event has been handled.
* @private
*/
protected handleEvent(payload: PlayerEvent & PlayerEvents): Promise<void>;
/**
* Emitted when a new track starts playing.
* @param {Player} player The player that started playing the track.
* @param {Track} track The track that started playing.
* @param {TrackStartEvent} payload The payload of the event emitted by the node.
* @private
*/
protected trackStart(player: Player, track: Track, payload: TrackStartEvent): void;
/**
* Emitted when a track ends playing.
* @param {Player} player - The player that the track ended on.
* @param {Track} track - The track that ended.
* @param {TrackEndEvent} payload - The payload of the event emitted by the node.
* @private
*/
protected trackEnd(player: Player, track: Track, payload: TrackEndEvent): Promise<void>;
/**
* Handles autoplay logic for a player.
* This method is responsible for selecting an appropriate method of autoplay
* and executing it. If autoplay is not enabled or all attempts have failed,
* it will return false.
* @param {Player} player - The player to handle autoplay for.
* @param {number} attempt - The current attempt number of the autoplay.
* @returns {Promise<boolean>} A promise that resolves to a boolean indicating if autoplay was successful.
* @private
*/
private handleAutoplay;
/**
* Selects a platform from the given enabled sources.
* @param {string[]} enabledSources - The enabled sources to select from.
* @returns {SearchPlatform | null} - The selected platform or null if none was found.
*/
selectPlatform(enabledSources: string[]): SearchPlatform | null;
/**
* Handles Last.fm-based autoplay.
* @param {Player} player - The player instance.
* @param {Track} previousTrack - The previous track.
* @param {SearchPlatform} platform - The selected platform.
* @param {string} apiKey - The Last.fm API key.
* @returns {Promise<boolean>} - Whether the autoplay was successful.
*/
private handlePlatformAutoplay;
/**
* Handles YouTube-based autoplay.
* @param {Player} player - The player instance.
* @param {Track} previousTrack - The previous track.
* @returns {Promise<boolean>} - Whether the autoplay was successful.
*/
private handleYouTubeAutoplay;
/**
* Handles the scenario when a track fails to play or load.
* Shifts the queue to the next track and emits a track end event.
* If there is no next track, handles the queue end scenario.
* If autoplay is enabled, plays the next track.
*
* @param {Player} player - The player instance associated with the track.
* @param {Track} track - The track that failed.
* @param {TrackEndEvent} payload - The event payload containing details about the track end.
* @returns {Promise<void>} A promise that resolves when the track failure has been processed.
* @private
*/
private handleFailedTrack;
/**
* Handles the scenario when a track is repeated.
* Shifts the queue to the next track and emits a track end event.
* If there is no next track, handles the queue end scenario.
* If autoplay is enabled, plays the next track.
*
* @param {Player} player - The player instance associated with the track.
* @param {Track} track - The track that is repeated.
* @param {TrackEndEvent} payload - The event payload containing details about the track end.
* @returns {Promise<void>} A promise that resolves when the repeated track has been processed.
* @private
*/
private handleRepeatedTrack;
/**
* Plays the next track in the queue.
* Updates the queue by shifting the current track to the previous track
* and plays the next track if autoplay is enabled.
*
* @param {Player} player - The player associated with the track.
* @param {Track} track - The track that has ended.
* @param {TrackEndEvent} payload - The event payload containing additional data about the track end event.
* @returns {void}
* @private
*/
private playNextTrack;
/**
* Handles the event when a queue ends.
* If autoplay is enabled, attempts to play the next track in the queue using the autoplay logic.
* If all attempts fail, resets the player state and emits the `queueEnd` event.
* @param {Player} player - The player associated with the track.
* @param {Track} track - The track that has ended.
* @param {TrackEndEvent} payload - The event payload containing additional data about the track end event.
* @returns {Promise<void>} A promise that resolves when the queue end processing is complete.
*/
queueEnd(player: Player, track: Track, payload: TrackEndEvent): Promise<void>;
/**
* Fetches the lyrics of a track from the Lavalink node.
* This method uses the `lavalyrics-plugin` to fetch the lyrics.
* If the plugin is not available, it will throw a RangeError.
*
* @param {Track} track - The track to fetch the lyrics for.
* @param {boolean} [skipTrackSource=false] - Whether to skip using the track's source URL.
* @returns {Promise<Lyrics>} A promise that resolves with the lyrics data.
*/
getLyrics(track: Track, skipTrackSource?: boolean): Promise<Lyrics>;
/**
* Handles the event when a track becomes stuck during playback.
* Stops the current track and emits a `trackStuck` event.
*
* @param {Player} player - The player associated with the track that became stuck.
* @param {Track} track - The track that became stuck.
* @param {TrackStuckEvent} payload - The event payload containing additional data about the track stuck event.
* @returns {void}
* @protected
*/
protected trackStuck(player: Player, track: Track, payload: TrackStuckEvent): Promise<void>;
/**
* Handles the event when a track has an error during playback.
* Stops the current track and emits a `trackError` event.
*
* @param {Player} player - The player associated with the track that had an error.
* @param {Track} track - The track that had an error.
* @param {TrackExceptionEvent} payload - The event payload containing additional data about the track error event.
* @returns {void}
* @protected
*/
protected trackError(player: Player, track: Track, payload: TrackExceptionEvent): Promise<void>;
/**
* Emitted when the WebSocket connection for a player closes.
* The payload of the event will contain the close code and reason if provided.
* @param {Player} player - The player associated with the WebSocket connection.
* @param {WebSocketClosedEvent} payload - The event payload containing additional data about the WebSocket close event.
*/
protected socketClosed(player: Player, payload: WebSocketClosedEvent): void;
/**
* Emitted when the segments for a track are loaded.
* The payload of the event will contain the segments.
* @param {Player} player - The player associated with the segments.
* @param {Track} track - The track associated with the segments.
* @param {SponsorBlockSegmentsLoaded} payload - The event payload containing additional data about the segments loaded event.
*/
private sponsorBlockSegmentLoaded;
/**
* Emitted when a segment of a track is skipped using the sponsorblock plugin.
* The payload of the event will contain the skipped segment.
* @param {Player} player - The player associated with the skipped segment.
* @param {Track} track - The track associated with the skipped segment.
* @param {SponsorBlockSegmentSkipped} payload - The event payload containing additional data about the segment skipped event.
*/
private sponsorBlockSegmentSkipped;
/**
* Emitted when chapters for a track are loaded using the sponsorblock plugin.
* The payload of the event will contain the chapters.
* @param {Player} player - The player associated with the chapters.
* @param {Track} track - The track associated with the chapters.
* @param {SponsorBlockChaptersLoaded} payload - The event payload containing additional data about the chapters loaded event.
*/
private sponsorBlockChaptersLoaded;
/**
* Emitted when a chapter of a track is started using the sponsorblock plugin.
* The payload of the event will contain the started chapter.
* @param {Player} player - The player associated with the started chapter.
* @param {Track} track - The track associated with the started chapter.
* @param {SponsorBlockChapterStarted} payload - The event payload containing additional data about the chapter started event.
*/
private sponsorBlockChapterStarted;
/**
* Fetches Lavalink node information.
* @returns {Promise<LavalinkInfo>} A promise that resolves to the Lavalink node information.
*/
fetchInfo(): Promise<LavalinkInfo>;
/**
* Gets the current sponsorblock segments for a player.
* @param {Player} player - The player to get the sponsorblocks for.
* @returns {Promise<SponsorBlockSegment[]>} A promise that resolves to the sponsorblock segments.
* @throws {RangeError} If the sponsorblock-plugin is not available in the Lavalink node.
*/
getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]>;
/**
* Sets the sponsorblock segments for a player.
* @param {Player} player - The player to set the sponsor blocks for.
* @param {SponsorBlockSegment[]} segments - The sponsorblock segments to set. Defaults to `[SponsorBlockSegment.Sponsor, SponsorBlockSegment.SelfPromo]` if not provided.
* @returns {Promise<void>} The promise is resolved when the operation is complete.
* @throws {RangeError} If the sponsorblock-plugin is not available in the Lavalink node.
* @throws {RangeError} If no segments are provided.
* @throws {SyntaxError} If an invalid sponsorblock is provided.
* @example
* ```ts
* // use it on the player via player.setSponsorBlock();
* player.setSponsorBlock([SponsorBlockSegment.Sponsor, SponsorBlockSegment.SelfPromo]);
* ```
*/
setSponsorBlock(player: Player, segments?: SponsorBlockSegment[]): Promise<void>;
/**
* Deletes the sponsorblock segments for a player.
* @param {Player} player - The player to delete the sponsorblocks for.
* @returns {Promise<void>} The promise is resolved when the operation is complete.
* @throws {RangeError} If the sponsorblock-plugin is not available in the Lavalink node.
*/
deleteSponsorBlock(player: Player): Promise<void>;
/**
* Creates a README.md or README.txt file in the magmastream directory
* if it doesn't already exist. This file is used to store player data
* for autoresume and other features.
* @private
*/
private createReadmeFile;
}
interface NodeOptions {
/** The host for the node. */
host: string;
/** The port for the node. */
port?: number;
/** The password for the node. */
password?: string;
/** Whether the host uses SSL. */
secure?: boolean;
/** The identifier for the node. */
identifier?: string;
/** The retryAmount for the node. */
retryAmount?: number;
/** The retryDelay for the node. */
retryDelay?: number;
/** Whether to resume the previous session. */
resumeStatus?: boolean;
/** The time the lavalink server will wait before it removes the player. */
resumeTimeout?: number;
/** The timeout used for api calls. */
requestTimeout?: number;
/** Priority of the node. */
priority?: number;
}
interface NodeStats {
/** The amount of players on the node. */
players: number;
/** The amount of playing players on the node. */
playingPlayers: number;
/** The uptime for the node. */
uptime: number;
/** The memory stats for the node. */
memory: MemoryStats;
/** The cpu stats for the node. */
cpu: CPUStats;
/** The frame stats for the node. */
frameStats: FrameStats;
}
interface MemoryStats {
/** The free memory of the allocated amount. */
free: number;
/** The used memory of the allocated amount. */
used: number;
/** The total allocated memory. */
allocated: number;
/** The reservable memory. */
reservable: number;
}
interface CPUStats {
/** The core amount the host machine has. */
cores: number;
/** The system load. */
systemLoad: number;
/** The lavalink load. */
lavalinkLoad: number;
}
interface FrameStats {
/** The amount of sent frames. */
sent?: number;
/** The amount of nulled frames. */
nulled?: number;
/** The amount of deficit frames. */
deficit?: number;
}
interface LavalinkInfo {
version: {
semver: string;
major: number;
minor: number;
patch: number;
preRelease: string;
};
buildTime: number;
git: {
branch: string;
commit: string;
commitTime: number;
};
jvm: string;
lavaplayer: string;
sourceManagers: string[];
filters: string[];
plugins: {
name: string;
version: string;
}[];
}
interface LyricsLine {
timestamp: number;
duration: number;
line: string;
plugin: object;
}
interface Lyrics {
source: string;
provider: string;
text?: string;
lines: LyricsLine[];
plugin: object[];
}
/**
* The player's queue, the `current` property is the currently playing track, think of the rest as the up-coming tracks.
*/
declare class Queue extends Array<Track> {
/**
* The total duration of the queue in milliseconds.
* This includes the duration of the currently playing track.
*/
get duration(): number;
/**
* The total size of tracks in the queue including the current track.
* This includes the current track if it is not null.
* @returns The total size of tracks in the queue including the current track.
*/
get totalSize(): number;
/**
* The size of tracks in the queue.
* This does not include the currently playing track.
* @returns The size of tracks in the queue.
*/
get size(): number;
/** The current track */
current: Track | null;
/** The previous tracks */
previous: Track[];
/** The Manager instance. */
manager: Manager;
/** The guild ID property. */
guildId: string;
/**
* Constructs a new Queue.
* @param guildId The guild ID.
* @param manager The Manager instance.
*/
constructor(guildId: string, manager: Manager);
/**
* Adds a track to the queue.
* @param track The track or tracks to add. Can be a single `Track` or an array of `Track`s.
* @param [offset=null] The position to add the track(s) at. If not provided, the track(s) will be added at the end of the queue.
*/
add(track: Track | Track[], offset?: number): void;
/**
* Removes track(s) from the queue.
* @param startOrPosition If a single number is provided, it will be treated as the position of the track to remove.
* If two numbers are provided, they will be used as the start and end of a range of tracks to remove.
* @param end Optional, end of the range of tracks to remove.
* @returns The removed track(s).
*/
remove(position?: number): Track[];
remove(start: number, end: number): Track[];
/**
* Clears the queue.
* This will remove all tracks from the queue and emit a state update event.
*/
clear(): void;
/**
* Shuffles the queue.
* This will randomize the order of the tracks in the queue and emit a state update event.
*/
shuffle(): void;
/**
* Shuffles the queue to play tracks requested by each user one block at a time.
*/
userBlockShuffle(): void;
/**
* Shuffles the queue to play tracks requested by each user one by one.
*/
roundRobinShuffle(): void;
}
declare abstract class TrackUtils {
static trackPartial: TrackPartial[] | null;
private static manager;
/**
* Initializes the TrackUtils class with the given manager.
* @param manager The manager instance to use.
* @hidden
*/
static init(manager: Manager): void;
/**
* Sets the partial properties for the Track class. If a Track has some of its properties removed by the partial,
* it will be considered a partial Track.
* @param {TrackPartial} partial The array of string property names to remove from the Track class.
*/
static setTrackPartial(partial: TrackPartial[]): void;
/**
* Checks if the provided argument is a valid Track.
* If provided an array then every element will be checked.
* @param trackOrTracks The Track or array of Tracks to check.
* @returns {boolean} Whether the provided argument is a valid Track.
*/
static validate(trackOrTracks: unknown): boolean;
/**
* Builds a Track from the raw data from Lavalink and a optional requester.
* @param data The raw data from Lavalink to build the Track from.
* @param requester The user who requested the track, if any.
* @returns The built Track.
*/
static build<T = User | ClientUser>(data: TrackData, requester?: T): Track;
}
/** Gets or extends structures to extend the built in, or already extended, classes to add more functionality. */
declare abstract class Structure {
/**
* Extends a class.
* @param name
* @param extender
*/
static extend<K extends keyof Extendable, T extends Extendable[K]>(name: K, extender: (target: Extendable[K]) => T): T;
/**
* Get a structure from available structures by name.
* @param name
*/
static get<K extends keyof Extendable>(name: K): Extendable[K];
}
type Sizes = "0" | "1" | "2" | "3" | "default" | "mqdefault" | "hqdefault" | "maxresdefault";
declare enum LoadTypes {
Track = "track",
Playlist = "playlist",
Search = "search",
Empty = "empty",
Error = "error"
}
type LoadType = keyof typeof LoadTypes;
declare enum StateTypes {
Connected = "CONNECTED",
Connecting = "CONNECTING",
Disconnected = "DISCONNECTED",
Disconnecting = "DISCONNECTING",
Destroying = "DESTROYING"
}
type State = keyof typeof StateTypes;
type SponsorBlockSegmentEvents = SponsorBlockSegmentSkipped | SponsorBlockSegmentsLoaded | SponsorBlockChapterStarted | SponsorBlockChaptersLoaded;
type SponsorBlockSegmentEventType = "SegmentSkipped" | "SegmentsLoaded" | "ChapterStarted" | "ChaptersLoaded";
type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;
type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent" | "SegmentSkipped" | "SegmentsLoaded" | "ChaptersLoaded" | "ChapterStarted";
declare enum TrackEndReasonTypes {
Finished = "finished",
LoadFailed = "loadFailed",
Stopped = "stopped",
Replaced = "replaced",
Cleanup = "cleanup"
}
type TrackEndReason = keyof typeof TrackEndReasonTypes;
declare enum SeverityTypes {
Common = "common",
Suspicious = "suspicious",
Fault = "fault"
}
type Severity = keyof typeof SeverityTypes;
interface TrackData {
/** The track information. */
encoded: string;
/** The detailed information of the track. */
info: TrackDataInfo;
/** Additional track info provided by plugins. */
pluginInfo: Record<string, string>;
}
interface TrackDataInfo {
identifier: string;
isSeekable: boolean;
author: string;
length: number;
isrc?: string;
isStream: boolean;
title: string;
uri?: string;
artworkUrl?: string;
sourceName?: TrackSourceName;
}
declare enum TrackSourceTypes {
AppleMusic = "applemusic",
Bandcamp = "bandcamp",
Deezer = "deezer",
Jiosaavn = "jiosaavn",
SoundCloud = "soundcloud",
Spotify = "spotify",
Tidal = "tidal",
VKMusic = "vkmusic",
YouTube = "youtube"
}
type TrackSourceName = keyof typeof TrackSourceTypes;
interface Extendable {
Player: typeof Player;
Queue: typeof Queue;
Node: typeof Node;
}
interface VoiceServer {
token: string;
guild_id: string;
endpoint: string;
}
interface VoiceState {
op: "voiceUpdate";
guildId: string;
event: VoiceServer;
sessionId?: string;
}
interface VoiceState {
guild_id: string;
user_id: string;
session_id: string;
channel_id: string;
}
interface VoicePacket {
t?: "VOICE_SERVER_UPDATE" | "VOICE_STATE_UPDATE";
d: VoiceState | VoiceServer;
}
interface NodeMessage extends NodeStats {
type: PlayerEventType;
op: "stats" | "playerUpdate" | "event";
guildId: string;
}
interface PlayerEvent {
op: "event";
type: PlayerEventType;
guildId: string;
}
interface Exception {
message: string;
severity: SeverityTypes;
cause: string;
}
interface TrackStartEvent extends PlayerEvent {
type: "TrackStartEvent";
track: TrackData;
}
interface TrackEndEvent extends PlayerEvent {
type: "TrackEndEvent";
track: TrackData;
reason: TrackEndReasonTypes;
}
interface TrackExceptionEvent extends PlayerEvent {
exception?: Exception;
guildId: string;
type: "TrackExceptionEvent";
}
interface TrackStuckEvent extends PlayerEvent {
type: "TrackStuckEvent";
thresholdMs: number;
}
interface WebSocketClosedEvent extends PlayerEvent {
type: "WebSocketClosedEvent";
code: number;
reason: string;
byRemote: boolean;
}
interface SponsorBlockSegmentsLoaded extends PlayerEvent {
type: "SegmentsLoaded";
segments: {
category: string;
start: number;
end: number;
}[];
}
interface SponsorBlockSegmentSkipped extends PlayerEvent {
type: "SegmentSkipped";
segment: {
category: string;
start: number;
end: number;
};
}
interface SponsorBlockChapterStarted extends PlayerEvent {
type: "ChapterStarted";
/** The chapter which started */
chapter: {
/** The name of the chapter */
name: string;
start: number;
end: number;
duration: number;
};
}
interface SponsorBlockChaptersLoaded extends PlayerEvent {
type: "ChaptersLoaded";
/** All chapters loaded */
chapters: {
/** The name of the chapter */
name: string;
start: number;
end: number;
duration: number;
}[];
}
interface PlayerUpdate {
op: "playerUpdate";
/** The guild id of the player. */
guildId: string;
state: {
/** Unix timestamp in milliseconds. */
time: number;
/** The position of the track in milliseconds. */
position: number;
/** Whether Lavalink is connected to the voice gateway. */
connected: boolean;
/** The ping of the node to the Discord voice server in milliseconds (-1 if not connected). */
ping: number;
};
}
/**
* The main hub for interacting with Lavalink and using Magmastream,
*/
declare class Manager extends EventEmitter {
/** The map of players. */
readonly players: Collection<string, Player>;
/** The map of nodes. */
readonly nodes: Collection<string, Node>;
/** The options that were set. */
readonly options: ManagerOptions;
initiated: boolean;
/**
* Initiates the Manager class.
* @param options
* @param options.plugins - An array of plugins to load.
* @param options.nodes - An array of node options to create nodes from.
* @param options.autoPlay - Whether to automatically play the first track in the queue when the player is created.
* @param options.autoPlaySearchPlatform - The search platform autoplay will use. Fallback to Youtube if not found.
* @param options.usePriority - Whether to use the priority when selecting a node to play on.
* @param options.clientName - The name of the client to send to Lavalink.
* @param options.defaultSearchPlatform - The default search platform to use when searching for tracks.
* @param options.useNode - The strategy to use when selecting a node to play on.
* @param options.trackPartial - The partial track search results to use when searching for tracks. This partials will always be presented on each track.
* @param options.eventBatchDuration - The duration to wait before processing the collected player state events.
* @param options.eventBatchInterval - The interval to wait before processing the collected player state events.
*/
constructor(options: ManagerOptions);
/**
* Initiates the Manager.
* @param clientId - The Discord client ID (required).
* @param clusterId - The cluster ID which runs the current process (required).
* @returns The manager instance.
*/
init(clientId: string, clusterId?: number): this;
/**
* Searches the enabled sources based off the URL or the `source` property.
* @param query
* @param requester
* @returns The search result.
*/
search<T = unknown>(query: string | SearchQuery, requester?: T): Promise<SearchResult>;
/**
* Creates a player or returns one if it already exists.
* @param options The options to create the player with.
* @returns The created player.
*/
create(options: PlayerOptions): Player;
/**
* Returns a player or undefined if it does not exist.
* @param guildId The guild ID of the player to retrieve.
* @returns The player if it exists, undefined otherwise.
*/
get(guildId: string): Player | undefined;
/**
* Destroys a player.
* @param guildId The guild ID of the player to destroy.
* @returns A promise that resolves when the player has been destroyed.
*/
destroy(guildId: string): Promise<void>;
/**
* Creates a new node or returns an existing one if it already exists.
* @param options - The options to create the node with.
* @returns The created node.
*/
createNode(options: NodeOptions): Node;
/**
* Destroys a node if it exists. Emits a debug event if the node is found and destroyed.
* @param identifier - The identifier of the node to destroy.
* @returns {void}
* @emits {debug} - Emits a debug message indicating the node is being destroyed.
*/
destroyNode(identifier: string): Promise<void>;
/**
* Attaches an event listener to the manager.
* @param event The event to listen for.
* @param listener The function to call when the event is emitted.
* @returns The manager instance for chaining.
*/
on<T extends keyof ManagerEvents>(event: T, listener: (...args: ManagerEvents[T]) => void): this;
/**
* Updates the voice state of a player based on the provided data.
* @param data - The data containing voice state information, which can be a VoicePacket, VoiceServer, or VoiceState.
* @returns A promise that resolves when the voice state update is handled.
* @emits {debug} - Emits a debug message indicating the voice state is being updated.
*/
updateVoiceState(data: VoicePacket | VoiceServer | VoiceState): Promise<void>;
/**
* Decodes an array of base64 encoded tracks and returns an array of TrackData.
* Emits a debug event with the tracks being decoded.
* @param tracks - An array of base64 encoded track strings.
* @returns A promise that resolves to an array of TrackData objects.
* @throws Will throw an error if no nodes are available or if the API request fails.
*/
decodeTracks(tracks: string[]): Promise<TrackData[]>;
/**
* Decodes a base64 encoded track and returns a TrackData.
* @param track - The base64 encoded track string.
* @returns A promise that resolves to a TrackData object.
* @throws Will throw an error if no nodes are available or if the API request fails.
*/
decodeTrack(track: string): Promise<TrackData>;
/**
* Saves player states to the JSON file.
* @param {string} guildId - The guild ID of the player to save
*/
savePlayerState(guildId: string): Promise<void>;
/**
* Loads player states from the JSON file.
* @param nodeId The ID of the node to load player states from.
* @returns A promise that resolves when the player states have been loaded.
*/
loadPlayerStates(nodeId: string): Promise<void>;
/**
* Returns the node to use based on the configured `useNode` and `usePriority` options.
* If `usePriority` is true, the node is chosen based on priority, otherwise it is chosen based on the `useNode` option.
* If `useNode` is "leastLoad", the node with the lowest load is chosen, if it is "leastPlayers", the node with the fewest players is chosen.
* If `usePriority` is false and `useNode` is not set, the node with the lowest load is chosen.
* @returns {Node} The node to use.
*/
get useableNode(): Node;
/**
* Handles the shutdown of the process by saving all active players' states and optionally cleaning up inactive players.
* This function is called when the process is about to exit.
* It iterates through all players and calls {@link savePlayerState} to save their states.
* Optionally, it also calls {@link cleanupInactivePlayers} to remove any stale player state files.
* After saving and cleaning up, it exits the process.
*/
handleShutdown(): Promise<void>;
/**
* Parses a YouTube title into a clean title and author.
* @param title - The original title of the YouTube video.
* @param originalAuthor - The original author of the YouTube video.
* @returns An object with the clean title and author.
*/
private parseYouTubeTitle;
/**
* Balances brackets in a given string by ensuring all opened brackets are closed correctly.
* @param str - The input string that may contain unbalanced brackets.
* @returns A new string with balanced brackets.
*/
private balanceBrackets;
/**
* Escapes a string by replacing special regex characters with their escaped counterparts.
* @param string - The string to escape.
* @returns The escaped string.
*/
private escapeRegExp;
/**
* Checks if the given data is a voice update.
* @param data The data to check.
* @returns Whether the data is a voice update.
*/
private isVoiceUpdate;
/**
* Determines if the provided update is a valid voice update.
* A valid update must contain either a token or a session_id.
*
* @param update - The voice update data to validate, which can be a VoicePacket, VoiceServer, or VoiceState.
* @returns {boolean} - True if the update is valid, otherwise false.
*/
private isValidUpdate;
/**
* Handles a voice server update by updating the player's voice state and sending the voice state to the Lavalink node.
* @param player The player for which the voice state is being updated.
* @param update The voice server data received from Discord.
* @returns A promise that resolves when the voice state update is handled.
* @emits {debug} - Emits a debug message indicating the voice state is being updated.
*/
private handleVoiceServerUpdate;
/**
* Handles a voice state update by updating the player's voice channel and session ID if provided, or by disconnecting and destroying the player if the channel ID is null.
* @param player The player for which the voice state is being updated.
* @param update The voice state data received from Discord.
* @emits {playerMove} - Emits a player move event if the channel ID is provided and the player is currently connected to a different voice channel.
* @emits {playerDisconnect} - Emits a player disconnect event if the channel ID is null.
*/
private handleVoiceStateUpdate;
/**
* Gets each player's JSON file
* @param {string} guildId - The guild ID
* @returns {string} The path to the player's JSON file
*/
private getPlayerFilePath;
/**
* Serializes a Player instance to avoid circular references.
* @param player The Player instance to serialize
* @returns The serialized Player instance
*/
private serializePlayer;
/**
* Checks for players that are no longer active and deletes their saved state files.
* This is done to prevent stale state files from accumulating on the file system.
*/
private cleanupInactivePlayers;
/**
* Returns the nodes that has the least load.
* The load is calculated by dividing the lavalink load by the number of cores.
* The result is multiplied by 100 to get a percentage.
* @returns {Collection<string, Node>}
*/
private get leastLoadNode();
/**
* Returns the nodes that have the least amount of players.
* Filters out disconnected nodes and sorts the remaining nodes
* by the number of players in ascending order.
* @returns {Collection<string, Node>} A collection of nodes sorted by player count.
*/
private get leastPlayersNode();
/**
* Returns a node based on priority.
* The nodes are sorted by priority in descending order, and then a random number
* between 0 and 1 is generated. The node that has a cumulative weight greater than or equal to the
* random number is returned.
* If no node has a cumulative weight greater than or equal to the random number, the node with the
* lowest load is returned.
* @returns {Node} The node to use.
*/
private get priorityNode();
}
interface Payload {
/** The OP code */
op: number;
d: {
guild_id: string;
channel_id: string | null;
self_mute: boolean;
self_deaf: boolean;
};
}
interface ManagerOptions {
/** Whether players should automatically play the next song. */
autoPlay?: boolean;
/** The search platform autoplay should use. Fallback to YouTube if not found.
* Use enum `SearchPlatform`. */
autoPlaySearchPlatform?: SearchPlatform;
/** The client ID to use. */
clientId?: string;
/** Value to use for the `Client-Name` header. */
clientName?: string;
/** The array of shard IDs connected to this manager instance. */
clusterId?: number;
/** The default search platform to use.
* Use enum `SearchPlatform`. */
defaultSearchPlatform?: SearchPlatform;
/** The last.fm API key.
* If you need to create one go here: https://www.last.fm/api/account/create.
* If you already have one, get it from here: https://www.last.fm/api/accounts. */
lastFmApiKey: string;
/** The maximum number of previous tracks to store. */
maxPreviousTracks?: number;
/** The array of nodes to connect to. */
nodes?: NodeOptions[];
/** A array of plugins to use. */
plugins?: Plugin[];
/** Whether the YouTube video titles should be replaced if the Author does not exactly match. */
replaceYouTubeCredentials?: boolean;
/** An array of track properties to keep. `track` will always be present. */
trackPartial?: TrackPartial[];
/** Use the least amount of players or least load? */
useNode?: UseNodeOptions.LeastLoad | UseNodeOptions.LeastPlayers;
/** Use priority mode over least amount of player or load? */
usePriority?: boolean;
/**
* Function to send data to the websocket.
* @param id The ID of the node to send the data to.
* @param payload The payload to send.
*/
send(id: string, payload: Payload): void;
}
declare enum TrackPartial {
/** The base64 encoded string of the track */
Track = "track",
/** The title of the track */
Title = "title",
/** The track identifier */
Identifier = "identifier",
/** The author of the track */
Author = "author",
/** The length of the track in milliseconds */
Duration = "duration",
/** The ISRC of the track */
Isrc = "isrc",
/** Whether the track is seekable */
IsSeekable = "isSeekable",
/** Whether the track is a stream */
IsStream = "isStream",
/** The URI of the track */
Uri = "uri",
/** The artwork URL of the track */
ArtworkUrl = "artworkUrl",
/** The source name of the track */
SourceName = "sourceName",
/** The thumbnail of the track */
ThumbNail = "thumbnail",
/** The requester of the track */
Requester = "requester",
/** The plugin info of the track */
PluginInfo = "pluginInfo",
/** The custom data of the track */
CustomData = "customData"
}
declare enum UseNodeOptions {
LeastLoad = "leastLoad",
LeastPlayers = "leastPlayers"
}
type UseNodeOption = keyof typeof UseNodeOptions;
declare enum SearchPlatform {
AppleMusic = "amsearch",
Bandcamp = "bcsearch",
Deezer = "dzsearch",
Jiosaavn = "jssearch",
SoundCloud = "scsearch",
Spotify = "spsearch",
Tidal = "tdsearch",
VKMusic = "vksearch",
YouTube = "ytsearch",
YouTubeMusic = "ytmsearch"
}
declare enum PlayerStateEventTypes {
AutoPlayChange = "playerAutoplay",
ConnectionChange = "playerConnection",
RepeatChange = "playerRepeat",
PauseChange = "playerPause",
QueueChange = "queueChange",
TrackChange = "trackChange",
VolumeChange = "volumeChange",
ChannelChange = "channelChange",
PlayerCreate = "playerCreate",
PlayerDestroy = "playerDestroy"
}
interface PlayerStateUpdateEvent {
changeType: PlayerStateEventTypes;
details?: AutoplayChangeEvent | ConnectionChangeEvent | RepeatChangeEvent | PauseChangeEvent | QueueChangeEvent | TrackChangeEvent | VolumeChangeEvent | Chan