Socket
Socket
Sign inDemoInstall

shoukaku

Package Overview
Dependencies
Maintainers
1
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shoukaku - npm Package Compare versions

Comparing version 3.3.1 to 3.4.0

dist/index.mjs.map

1207

dist/index.d.ts

@@ -1,11 +0,1196 @@

export * as Connectors from './src/connectors/libs';
export * as Constants from './src/Constants';
export * as Utils from './src/Utils';
export * from './src/connectors/Connector';
export * from './src/guild/Connection';
export * from './src/guild/Player';
export * from './src/node/Node';
export * from './src/node/Queue';
export * from './src/node/Rest';
export * from './src/Shoukaku';
//# sourceMappingURL=index.d.ts.map
import { EventEmitter } from 'events';
import Websocket from 'ws';
declare enum State {
CONNECTING = 0,
CONNECTED = 1,
DISCONNECTING = 2,
DISCONNECTED = 3
}
declare enum VoiceState {
SESSION_READY = 0,
SESSION_ID_MISSING = 1,
SESSION_ENDPOINT_MISSING = 2
}
declare enum OPCodes {
VOICE_UPDATE = "voiceUpdate",
PLAY = "play",
STOP = "stop",
PAUSE = "pause",
SEEK = "seek",
VOLUME = "volume",
FILTERS = "filters",
DESTROY = "destroy",
PLAYER_UPDATE = "playerUpdate",
CONFIGURE_RESUMING = "configureResuming",
STATS = "stats",
EVENT = "event"
}
declare const ShoukakuDefaults: ShoukakuOptions;
declare const NodeDefaults: NodeOption;
declare const Constants_NodeDefaults: typeof NodeDefaults;
type Constants_OPCodes = OPCodes;
declare const Constants_OPCodes: typeof OPCodes;
declare const Constants_ShoukakuDefaults: typeof ShoukakuDefaults;
type Constants_State = State;
declare const Constants_State: typeof State;
type Constants_VoiceState = VoiceState;
declare const Constants_VoiceState: typeof VoiceState;
declare namespace Constants {
export {
Constants_NodeDefaults as NodeDefaults,
Constants_OPCodes as OPCodes,
Constants_ShoukakuDefaults as ShoukakuDefaults,
Constants_State as State,
Constants_VoiceState as VoiceState,
};
}
/**
* Represents the partial payload from a stateUpdate event
*/
interface StateUpdatePartial {
channel_id?: string;
session_id?: string;
self_deaf: boolean;
self_mute: boolean;
}
/**
* Represents the payload from a serverUpdate event
*/
interface ServerUpdate {
token: string;
guild_id: string;
endpoint: string;
}
/**
* Represents a connection to a Discord voice channel
*/
declare class Connection extends EventEmitter {
/**
* An instance of the Player class
*/
readonly player: Player;
/**
* ID of Guild that contains the connected voice channel
*/
guildId: string;
/**
* ID of the connected voice channel
*/
channelId: string | null;
/**
* ID of the Shard that contains the guild that contains the connected voice channel
*/
shardId: number;
/**
* ID of current session
*/
sessionId: string | null;
/**
* Region of connected voice channel
*/
region: string | null;
/**
* Mute status in connected voice channel
*/
muted: boolean;
/**
* Deafen status in connected voice channel
*/
deafened: boolean;
/**
* Connection state
*/
state: State;
/**
* Boolean that indicates if voice channel changed since initial connection
*/
moved: boolean;
/**
* Boolean that indicates if this instance is reconnecting
*/
reconnecting: boolean;
/**
* Cached serverUpdate event from Lavalink
*/
private serverUpdate;
/**
* @param player Shoukaku Player class
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
*/
constructor(player: Player, options: VoiceChannelOptions);
/**
* Set the deafen status for the current bot user
* @param deaf Boolean value to indicate whether to deafen or undeafen
* @defaultValue false
*/
setDeaf(deaf?: boolean): void;
/**
* Set the mute status for the current bot user
* @param mute Boolean value to indicate whether to mute or unmute
* @defaultValue false
*/
setMute(mute?: boolean): void;
/**
* Disconnect the current bot user from the connected voice channel
*/
disconnect(): void;
/**
* Connect the current bot user to a voice channel
*
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Unused parameter
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen or undeafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute or unmute the current bot user
*/
connect(options: VoiceChannelOptions): Promise<void>;
/**
* Update Session ID, Channel ID, Deafen status and Mute status of this instance
*
* @param options.session_id ID of this session
* @param options.channel_id ID of currently connected voice channel
* @param options.self_deaf Boolean that indicates if the current bot user is deafened or not
* @param options.self_mute Boolean that indicates if the current bot user is muted or not
* @internal
*/
setStateUpdate(options: StateUpdatePartial): void;
/**
* Send voiceUpdate event to Lavalink and also cache the serverUpdate event from Discord
* @internal
*/
setServerUpdate(data: ServerUpdate): void;
/**
* Send voiceUpdate to Lavalink again
* @internal
*/
resendServerUpdate(): void;
/**
* Destroy the current Lavalink player
*/
destroyLavalinkPlayer(): void;
/**
* Send data to Discord
* @param data The data to send
* @param important Whether to prioritize sending this packet in the queue
* @internal
*/
private send;
}
type TrackEndReason = 'FINISHED' | 'LOAD_FAILED' | 'STOPPED' | 'REPLACED' | 'CLEANUP';
type Severity = 'COMMON' | 'SUSPICIOUS' | 'FAULT';
type PlayerEventType = 'TrackStartEvent' | 'TrackEndEvent' | 'TrackExceptionEvent' | 'TrackStuckEvent' | 'WebSocketClosedEvent';
/**
* Options when playing a new track
*/
interface PlayOptions {
track: string;
options?: {
noReplace?: boolean;
pause?: boolean;
startTime?: number;
endTime?: number;
};
}
interface PlayPayload {
op: string;
guildId: string;
track: string;
noReplace: boolean;
pause: boolean;
startTime?: number;
endTime?: number;
}
interface ResumeOptions {
noReplace?: boolean;
pause?: boolean;
startTime?: number;
endtime?: number;
}
interface Band {
band: number;
gain: number;
}
interface KaraokeSettings {
level?: number;
monoLevel?: number;
filterBand?: number;
filterWidth?: number;
}
interface TimescaleSettings {
speed?: number;
pitch?: number;
rate?: number;
}
interface FreqSettings {
frequency?: number;
depth?: number;
}
interface RotationSettings {
rotationHz?: number;
}
interface DistortionSettings {
sinOffset?: number;
sinScale?: number;
cosOffset?: number;
cosScale?: number;
tanOffset?: number;
tanScale?: number;
offset?: number;
scale?: number;
}
interface ChannelMixSettings {
leftToLeft?: number;
leftToRight?: number;
rightToLeft?: number;
rightToRight?: number;
}
interface LowPassSettings {
smoothing?: number;
}
interface PlayerEvent {
op: OPCodes.EVENT;
type: PlayerEventType;
guildId: string;
}
interface Exception {
severity: Severity;
message: string;
cause: string;
}
interface TrackStartEvent extends PlayerEvent {
type: 'TrackStartEvent';
track: string;
}
interface TrackEndEvent extends PlayerEvent {
type: 'TrackEndEvent';
track: string;
reason: TrackEndReason;
}
interface TrackExceptionEvent extends PlayerEvent {
type: 'TrackExceptionEvent';
exception?: Exception;
error: string;
}
interface TrackStuckEvent extends PlayerEvent {
type: 'TrackStuckEvent';
track: string;
thresholdMs: number;
}
interface TrackStuckEvent extends PlayerEvent {
type: 'TrackStuckEvent';
thresholdMs: number;
}
interface WebSocketClosedEvent extends PlayerEvent {
type: 'WebSocketClosedEvent';
code: number;
byRemote: boolean;
reason: string;
}
interface PlayerUpdate {
op: OPCodes.PLAYER_UPDATE;
state: {
connected: boolean;
position?: number;
time: number;
};
guildId: string;
}
interface FilterOptions {
volume?: number;
equalizer?: Band[];
karaoke?: KaraokeSettings | null;
timescale?: TimescaleSettings | null;
tremolo?: FreqSettings | null;
vibrato?: FreqSettings | null;
rotation?: RotationSettings | null;
distortion?: DistortionSettings | null;
channelMix?: ChannelMixSettings | null;
lowPass?: LowPassSettings | null;
}
/**
* Lavalink filters
*/
declare class Filters {
volume: number;
equalizer: Band[];
karaoke: KaraokeSettings | null;
timescale: TimescaleSettings | null;
tremolo: FreqSettings | null;
vibrato: FreqSettings | null;
rotation: RotationSettings | null;
distortion: DistortionSettings | null;
channelMix: ChannelMixSettings | null;
lowPass: LowPassSettings | null;
/**
* Options to initialize this filters instance with
* @param options.volume The volume to play audio at as a decimal
* @param options.equalizer An array of objects that conforms to the Bands type that define volumes at different frequencies
* @param options.karaoke An object that conforms to the KaraokeSettings type that defines a range of frequencies to mute
* @param options.timescale An object that conforms to the TimescaleSettings type that defines the time signature to play the audio at
* @param options.tremolo An object that conforms to the FreqSettings type that defines an ocillation in volume
* @param options.vibrato An object that conforms to the FreqSettings type that defines an ocillation in pitch
* @param options.rotation An object that conforms to the RotationSettings type that defines the frequency of audio rotating round the listener
* @param options.distortion An object that conforms to DistortionSettings that defines distortions in the audio
* @param options.channelMix An object that conforms to ChannelMixSettings that defines how much the left and right channels affect each other (setting all factors to 0.5 causes both channels to get the same audio)
* @param options.lowPass An object that conforms to LowPassSettings that defines the amount of suppression on higher frequencies
*/
constructor(options?: FilterOptions);
}
declare interface Player {
/**
* Emitted when the current playing track ends
* @eventProperty
*/
on(event: 'end', listener: (reason: TrackEndEvent) => void): this;
/**
* Emitted when the current playing track gets stuck due to an error
* @eventProperty
*/
on(event: 'stuck', listener: (data: TrackStuckEvent) => void): this;
/**
* Emitted when the current websocket connection is closed
* @eventProperty
*/
on(event: 'closed', listener: (reason: WebSocketClosedEvent) => void): this;
/**
* Emitted when a new track starts
* @eventProperty
*/
on(event: 'start', listener: (data: TrackStartEvent) => void): this;
/**
* Emitted when there is an error caused by the current playing track
* @eventProperty
*/
on(event: 'exception', listener: (reason: TrackExceptionEvent) => void): this;
/**
* Emitted when the library manages to resume the player
* @eventProperty
*/
on(event: 'resumed', listener: () => void): this;
/**
* Emitted when a playerUpdate even is recieved from Lavalink
* @eventProperty
*/
on(event: 'update', listener: (data: PlayerUpdate) => void): this;
once(event: 'end', listener: (reason: TrackEndEvent) => void): this;
once(event: 'stuck', listener: (data: TrackStuckEvent) => void): this;
once(event: 'closed', listener: (reason: WebSocketClosedEvent) => void): this;
once(event: 'start', listener: (data: TrackStartEvent) => void): this;
once(event: 'exception', listener: (reason: TrackExceptionEvent) => void): this;
once(event: 'resumed', listener: () => void): this;
once(event: 'update', listener: (data: PlayerUpdate) => void): this;
off(event: 'end', listener: (reason: TrackEndEvent) => void): this;
off(event: 'stuck', listener: (data: TrackStuckEvent) => void): this;
off(event: 'closed', listener: (reason: WebSocketClosedEvent) => void): this;
off(event: 'start', listener: (data: TrackStartEvent) => void): this;
off(event: 'exception', listener: (reason: TrackExceptionEvent) => void): this;
off(event: 'resumed', listener: () => void): this;
off(event: 'update', listener: (data: PlayerUpdate) => void): this;
}
/**
* Wrapper object around Lavalink
*/
declare class Player extends EventEmitter {
/**
* Lavalink node this player is connected to
*/
node: Node;
/**
* Discort voice channel that this player is connected to
*/
readonly connection: Connection;
/**
* ID of current track
*/
track: string | null;
/**
* Pause status in current player
*/
paused: boolean;
/**
* Ping represents the number of milliseconds between heartbeat and ack. Could be `-1` if not connected
*/
ping: number;
/**
* Position in ms of current track
*/
position: number;
/**
* Filters on current track
*/
filters: Filters;
/**
* @param node An instance of Node (Lavalink API wrapper)
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
*/
constructor(node: Node, options: VoiceChannelOptions);
/**
* Move player to another node
* @param name Name of node to move to
* @returns The current player instance
*/
move(name: string): Player;
/**
* Play a new track
* @param playable Options for playing this track
* @returns The current player instance
*/
playTrack(playable: PlayOptions): Player;
/**
* Stop the currently playing track
* @returns The current player instance
*/
stopTrack(): Player;
/**
* Pause or unpause the currently playing track
* @param pause Boolean value to specify whether to pause or unpause the current bot user
* @returns The current player instance
*/
setPaused(pause?: boolean): Player;
/**
* Seek to a specific time in the currently playing track
* @param position Position to seek to in milliseconds
* @returns The current player instance
*/
seekTo(position: number): Player;
/**
* Change the volume of the currently playing track
* @param volume Target volume as a decimal
* @returns The current player instance
*/
setVolume(volume: number): Player;
/**
* Change the equalizer settings applied to the currently playing track
* @param bands An array of objects that conforms to the Bands type that define volumes at different frequencies
* @returns The current player instance
*/
setEqualizer(bands: Band[]): Player;
/**
* Change the karaoke settings applied to the currently playing track
* @param karaoke An object that conforms to the KaraokeSettings type that defines a range of frequencies to mute
* @returns The current player instance
*/
setKaraoke(karaoke?: KaraokeSettings): Player;
/**
* Change the timescale settings applied to the currently playing track
* @param timescale An object that conforms to the TimescaleSettings type that defines the time signature to play the audio at
* @returns The current player instance
*/
setTimescale(timescale?: TimescaleSettings): Player;
/**
* Change the tremolo settings applied to the currently playing track
* @param tremolo An object that conforms to the FreqSettings type that defines an ocillation in volume
* @returns The current player instance
*/
setTremolo(tremolo?: FreqSettings): Player;
/**
* Change the vibrato settings applied to the currently playing track
* @param vibrato An object that conforms to the FreqSettings type that defines an ocillation in pitch
* @returns The current player instance
*/
setVibrato(vibrato?: FreqSettings): Player;
/**
* Change the rotation settings applied to the currently playing track
* @param rotation An object that conforms to the RotationSettings type that defines the frequency of audio rotating round the listener
* @returns The current player instance
*/
setRotation(rotation?: RotationSettings): Player;
/**
* Change the distortion settings applied to the currently playing track
* @param distortion An object that conforms to DistortionSettings that defines distortions in the audio
* @returns The current player instance
*/
setDistortion(distortion: DistortionSettings): Player;
/**
* Change the channel mix settings applied to the currently playing track
* @param mix An object that conforms to ChannelMixSettings that defines how much the left and right channels affect each other (setting all factors to 0.5 causes both channels to get the same audio)
* @returns The current player instance
*/
setChannelMix(mix: ChannelMixSettings): Player;
/**
* Change the low pass settings applied to the currently playing track
* @param pass An object that conforms to LowPassSettings that defines the amount of suppression on higher frequencies
* @returns The current player instance
*/
setLowPass(pass: LowPassSettings): Player;
/**
* Change the all filter settings applied to the currently playing track
* @param options An object that conforms to FilterOptions that defines all filters to apply/modify
* @returns The current player instance
*/
setFilters(options: FilterOptions): Player;
/**
* Clear all filters applied to the currently playing track
* @returns The current player instance
*/
clearFilters(): Player;
/**
* Resume the current track
* @param options An object that conforms to ResumeOptions that specify behavior on resuming
* @returns The current player instance
*/
resume(options?: ResumeOptions): Player;
/**
* Update all filters via the filters operation
* @internal
*/
private updateFilters;
/**
* Remove all event listeners on this instance
* @internal
*/
clean(): void;
/**
* Reset the track, position and filters on this instance to defaults
*/
reset(): void;
/**
* Handle JSON data recieved from Lavalink
* @param json JSON data from Lavalink
* @internal
*/
onLavalinkMessage(json: any): void;
/**
* Handle player events recieved from Lavalink
* @param json JSON data from Lavalink
* @internal
*/
private onPlayerEvent;
}
/**
* Represents a message queue
* @internal
*/
declare class Queue {
/**
* Node that initalized this message queue
*/
private readonly node;
/**
* Pending messages
*/
readonly pending: string[];
/**
* Number of flushes in this queue
*/
private flushes;
/**
* @param node An instance of Node
*/
constructor(node: Node);
/**
* Add data to queue
* @param data Message data
* @param important Priority status
* @internal
*/
add(data?: any, important?: boolean): void;
/**
* Clear the queue
* @internal
*/
clear(): void;
/**
* Flush the ws connections in queue
* @param code Status code
* @param reason Reason for close
* @internal
*/
flush(code: number, reason?: string): void;
/**
* Process messages in the queue
* @internal
*/
protected process(): void;
}
type LoadType = 'TRACK_LOADED' | 'PLAYLIST_LOADED' | 'SEARCH_RESULT' | 'NO_MATCHES' | 'LOAD_FAILED';
interface Track {
track: string;
info: {
identifier: string;
isSeekable: boolean;
author: string;
length: number;
isStream: boolean;
position: number;
title: string;
uri: string;
sourceName: string;
};
}
interface LavalinkResponse {
loadType: LoadType;
playlistInfo: {
name?: string;
selectedTrack?: number;
};
tracks: Track[];
}
interface Address {
address: string;
failingTimestamp: number;
failingTime: string;
}
interface RoutePlanner {
class?: string;
details?: {
ipBlock: {
type: string;
size: string;
};
failingAddresses: Address[];
};
rotateIndex?: string;
ipIndex?: string;
currentAddress?: string;
blockIndex?: string;
currentAddressIndex?: string;
}
/**
* Wrapper around Lavalink REST API
*/
declare class Rest {
/**
* Node that initialized this instance
*/
protected readonly node: Node;
/**
* URL of Lavalink
*/
protected readonly url: string;
/**
* Credentials to access Lavalink
*/
protected readonly auth: string;
/**
* @param node An instance of Node
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Weather to use secure protocols or not
* @param options.group Group of this node
*/
constructor(node: Node, options: NodeOption);
/**
* Resolve a track
* @param identifier Track ID
* @returns A promise that resolves to a Lavalink response or void
*/
resolve(identifier: string): Promise<LavalinkResponse | null>;
/**
* Decode a track
* @param track Encoded track
* @returns Promise that resolves to a track or void
*/
decode(track: string): Promise<Track | null>;
/**
* Get routplanner status from Lavalink
* @returns Promise that resolves to a routeplanner response or void
* @internal
*/
getRoutePlannerStatus(): Promise<RoutePlanner | null>;
/**
* Release blacklisted IP address into pool of IPs
* @param address IP address
* @internal
*/
unmarkFailedAddress(address: string): Promise<void | null>;
/**
* Make a request to Lavalink
* @param fetchOptions.endpoint Lavalink endpoint
* @param fetchOptions.options Options passed to fetch
* @internal
*/
private fetch;
}
interface VoiceChannelOptions {
guildId: string;
shardId: number;
channelId: string;
deaf?: boolean;
mute?: boolean;
}
interface NodeStats {
players: number;
playingPlayers: number;
memory: {
reservable: number;
used: number;
free: number;
allocated: number;
};
frameStats: {
sent: number;
deficit: number;
nulled: number;
};
cpu: {
cores: number;
systemLoad: number;
lavalinkLoad: number;
};
uptime: number;
}
interface ResumableHeaders {
[key: string]: string;
'Client-Name': string;
'User-Agent': string;
'Authorization': string;
'User-Id': string;
'Resume-Key': string;
}
interface NonResumableHeaders {
[key: string]: string;
'Client-Name': string;
'User-Agent': string;
'Authorization': string;
'User-Id': string;
}
/**
* Represents a Lavalink node
*/
declare class Node extends EventEmitter {
/**
* Shoukaku class
*/
readonly manager: Shoukaku;
/**
* A map of guild ID to players
*/
readonly players: Map<string, Player>;
/**
* Lavalink rest API
*/
readonly rest: Rest;
/**
* Lavalink API message queue
*/
readonly queue: Queue;
/**
* Name of this node
*/
readonly name: string;
/**
* Group in which this node is contained
*/
readonly group?: string;
/**
* URL of Lavalink
*/
private readonly url;
/**
* Credentials to access Lavalink
*/
private readonly auth;
/**
* The number of reconnects to Lavalink
*/
reconnects: number;
/**
* The state of this connection
*/
state: State;
/**
* Statistics from Lavalink
*/
stats: NodeStats | null;
/**
* Websocket instance
*/
ws: Websocket | null;
/**
* Boolean that represents if the node has initialized once (will always be true when alwaysSendResumeKey is true)
*/
protected initialized: boolean;
/**
* Boolean that represents if this connection is destroyed
*/
protected destroyed: boolean;
/**
* @param manager Shoukaku instance
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Whether to use secure protocols or not
* @param options.group Group of this node
*/
constructor(manager: Shoukaku, options: NodeOption);
/**
* Penalties for load balancing
* @returns Penalty score
* @internal @readonly
*/
get penalties(): number;
/**
* Connect to Lavalink
*/
connect(): void;
/**
* Disconnect from lavalink
* @param code Status code
* @param reason Reason for disconnect
*/
disconnect(code: number, reason?: string): void;
/**
* Join a voice channel in a guild
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
* @returns A promise that resolves to a player class
*/
joinChannel(options: VoiceChannelOptions): Promise<Player>;
/**
* Disconnect from connected voice channel
* @param guildId ID of guild that contains voice channel
*/
leaveChannel(guildId: string): void;
/**
* Handle connection open event from Lavalink
* @param response Response from Lavalink
* @param reconnect Whether to reconnect on failure
* @internal
*/
private open;
/**
* Handle message from Lavalink
* @param message JSON message
* @internal
*/
private message;
/**
* Handle closed event from lavalink
* @param code Status close
* @param reason Reason for connection close
*/
private close;
/**
* Destroys the websocket connection
* @internal
*/
private destroy;
/**
* Clear message queue and move players to other nodes if possible
* @internal
*/
private clean;
/**
* Reconnect to Lavalink
* @internal
*/
private reconnect;
/**
* Handle raw message from Discord
* @param packet Packet data
* @internal
*/
discordRaw(packet: any): void;
}
type Constructor<T> = new (...args: any[]) => T;
/**
* Merge the default options to user input
* @param def Default options
* @param given User input
* @returns Merged options
*/
declare function mergeDefault<T extends {
[key: string]: any;
}>(def: T, given: T): Required<T>;
/**
* Wait for a specific amount of time (timeout)
* @param ms Time to wait in milliseconds
* @returns A promise that resolves in x seconds
*/
declare function wait(ms: number): Promise<void>;
type Utils_Constructor<T> = Constructor<T>;
declare const Utils_mergeDefault: typeof mergeDefault;
declare const Utils_wait: typeof wait;
declare namespace Utils {
export {
Utils_Constructor as Constructor,
Utils_mergeDefault as mergeDefault,
Utils_wait as wait,
};
}
interface Structures {
/**
* A custom structure that extends the Rest class
*/
rest?: Constructor<Rest>;
/**
* A custom structure that extends the Player class
*/
player?: Constructor<Player>;
}
interface NodeOption {
/**
* Name of this node
*/
name: string;
/**
* URL of Lavalink
*/
url: string;
/**
* Credentials to access Lavalnk
*/
auth: string;
/**
* Whether to use secure protocols or not
*/
secure?: boolean;
/**
* Group of this node
*/
group?: string;
}
interface ShoukakuOptions {
/**
* Whether to resume a connection on disconnect to Lavalink (Server Side) (Note: DOES NOT RESUME WHEN THE LAVALINK SERVER DIES)
*/
resume?: boolean;
/**
* Resume key for Lavalink
*/
resumeKey?: string;
/**
* Time to wait before lavalink starts to destroy the players of the disconnected client
*/
resumeTimeout?: number;
/**
* Whether to resume the players by doing it in the library side (Client Side) (Note: TRIES TO RESUME REGARDLESS OF WHAT HAPPENED ON A LAVALINK SERVER)
*/
resumeByLibrary?: boolean;
/**
* Disables the first time initialization tracking of nodes, and just sends the resume key always (Note: Useful for people who save their players to redis and wants to resume sessions even at first boot)
*/
alwaysSendResumeKey?: boolean;
/**
* Number of times to try and reconnect to Lavalink before giving up
*/
reconnectTries?: number;
/**
* Timeout before trying to reconnect
*/
reconnectInterval?: number;
/**
* Time to wait for a response from the Lavalink REST API before giving up
*/
restTimeout?: number;
/**
* Whether to move players to a different Lavalink node when a node disconnects
*/
moveOnDisconnect?: boolean;
/**
* User Agent to use when making requests to Lavalink
*/
userAgent?: string;
/**
* Custom structures for shoukaku to use
*/
structures?: Structures;
}
interface MergedShoukakuOptions {
resume: boolean;
resumeKey: string;
resumeTimeout: number;
resumeByLibrary: boolean;
alwaysSendResumeKey: boolean;
reconnectTries: number;
reconnectInterval: number;
restTimeout: number;
moveOnDisconnect: boolean;
userAgent: string;
structures: Structures;
}
declare interface Shoukaku {
/**
* Emitted when reconnect tries are occurring and how many tries are left
* @eventProperty
*/
on(event: 'reconnecting', listener: (name: string, info: string, tries: number, triesLeft: number, reconnectInterval: number) => void): this;
/**
* Emitted when data useful for debugging is produced
* @eventProperty
*/
on(event: 'debug', listener: (name: string, info: string) => void): this;
/**
* Emitted when an error occurs
* @eventProperty
*/
on(event: 'error', listener: (name: string, error: Error) => void): this;
/**
* Emitted when Shoukaku is ready to recieve operations
* @eventProperty
*/
on(event: 'ready', listener: (name: string, reconnected: boolean) => void): this;
/**
* Emitted when a websocket connection to Lavalink closes
* @eventProperty
*/
on(event: 'close', listener: (name: string, code: number, reason: string) => void): this;
/**
* Emitted when a websocket connection to Lavalink disconnects
* @eventProperty
*/
on(event: 'disconnect', listener: (name: string, players: Player[], moved: boolean) => void): this;
/**
* Emitted when a raw message is recived from Lavalink
* @eventProperty
*/
on(event: 'raw', listener: (name: string, json: unknown) => void): this;
once(event: 'reconnecting', listener: (name: string, reconnectsLeft: number, reconnectInterval: number) => void): this;
once(event: 'debug', listener: (name: string, info: string) => void): this;
once(event: 'error', listener: (name: string, error: Error) => void): this;
once(event: 'ready', listener: (name: string, reconnected: boolean) => void): this;
once(event: 'close', listener: (name: string, code: number, reason: string) => void): this;
once(event: 'disconnect', listener: (name: string, players: Player[], moved: boolean) => void): this;
once(event: 'raw', listener: (name: string, json: unknown) => void): this;
off(event: 'reconnecting', listener: (name: string, reconnectsLeft: number, reconnectInterval: number) => void): this;
off(event: 'debug', listener: (name: string, info: string) => void): this;
off(event: 'error', listener: (name: string, error: Error) => void): this;
off(event: 'ready', listener: (name: string, reconnected: boolean) => void): this;
off(event: 'close', listener: (name: string, code: number, reason: string) => void): this;
off(event: 'disconnect', listener: (name: string, players: Player[], moved: boolean) => void): this;
off(event: 'raw', listener: (name: string, json: unknown) => void): this;
}
/**
* Main Shoukaku class
*/
declare class Shoukaku extends EventEmitter {
/**
* Discord library connector
*/
readonly connector: Connector;
/**
* Shoukaku options
*/
readonly options: MergedShoukakuOptions;
/**
* Connected Lavalink nodes
*/
readonly nodes: Map<string, Node>;
/**
* Shoukaku instance identifier
*/
id: string | null;
/**
* @param connector A Discord library connector
* @param nodes An array that conforms to the NodeOption type that specifies nodes to connect to
* @param options.resume Whether to resume a connection on disconnect to Lavalink (Server Side) (Note: DOES NOT RESUME WHEN THE LAVALINK SERVER DIES)
* @param options.resumeKey Resume key for Lavalink
* @param options.resumeTimeout Time to wait before lavalink starts to destroy the players of the disconnected client
* @param options.resumeByLibrary Whether to resume the players by doing it in the library side (Client Side) (Note: TRIES TO RESUME REGARDLESS OF WHAT HAPPENED ON A LAVALINK SERVER)
* @param options.alwaysSendResumeKey Disables the first time initialization tracking of nodes, and just sends the resume key always (Note: Useful for people who save their players to redis and wants to resume sessions even at first boot)
* @param options.reconnectTries Number of times to try and reconnect to Lavalink before giving up
* @param options.reconnectInterval Timeout before trying to reconnect
* @param options.restTimeout Time to wait for a response from the Lavalink REST API before giving up
* @param options.moveOnDisconnect Whether to move players to a different Lavalink node when a node disconnects
* @param options.userAgent User Agent to use when making requests to Lavalink
* @param options.structures Custom structures for shoukaku to use
*/
constructor(connector: Connector, nodes: NodeOption[], options?: ShoukakuOptions);
/**
* Get a list of players
* @returns A map of guild IDs and players
* @readonly
*/
get players(): Map<string, Player>;
/**
* Add a Lavalink node to the pool of available nodes
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Whether to use secure protocols or not
* @param options.group Group of this node
*/
addNode(options: NodeOption): void;
/**
* Remove a Lavalink node from the pool of available nodes
* @param name Name of the node
* @param reason Reason of removing the node
*/
removeNode(name: string, reason?: string): void;
/**
* Select a Lavalink node from the pool of nodes
* @param name A specific node, an array of nodes, or the string `auto`
* @returns A Lavalink node or undefined
*/
getNode(name?: string | string[]): Node | undefined;
/**
* Get the Lavalink node the least penalty score
* @param group A group, an array of groups, or the string `auto`
* @returns A Lavalink node or undefined
* @internal
*/
private getIdeal;
}
interface ConnectorMethods {
sendPacket: any;
getId: any;
}
declare abstract class Connector {
protected readonly client: any;
protected manager: Shoukaku | null;
constructor(client: any);
set(manager: Shoukaku): Connector;
protected ready(nodes: NodeOption[]): void;
protected raw(packet: any): void;
abstract getId(): string;
abstract sendPacket(shardId: number, payload: any, important: boolean): void;
abstract listen(nodes: NodeOption[]): void;
}
declare class DiscordJS extends Connector {
sendPacket(shardId: number, payload: any, important: boolean): void;
getId(): string;
listen(nodes: NodeOption[]): void;
}
declare class Eris extends Connector {
sendPacket(shardId: number, payload: any, important: boolean): void;
getId(): string;
listen(nodes: NodeOption[]): void;
}
declare class OceanicJS extends Connector {
sendPacket(shardId: number, payload: any, important: boolean): void;
getId(): string;
listen(nodes: NodeOption[]): void;
}
type index_DiscordJS = DiscordJS;
declare const index_DiscordJS: typeof DiscordJS;
type index_Eris = Eris;
declare const index_Eris: typeof Eris;
type index_OceanicJS = OceanicJS;
declare const index_OceanicJS: typeof OceanicJS;
declare namespace index {
export {
index_DiscordJS as DiscordJS,
index_Eris as Eris,
index_OceanicJS as OceanicJS,
};
}
export { Address, Band, ChannelMixSettings, Connection, Connector, ConnectorMethods, index as Connectors, Constants, DistortionSettings, Exception, FilterOptions, Filters, FreqSettings, KaraokeSettings, LavalinkResponse, LoadType, LowPassSettings, MergedShoukakuOptions, Node, NodeOption, NodeStats, NonResumableHeaders, PlayOptions, PlayPayload, Player, PlayerEvent, PlayerEventType, PlayerUpdate, Queue, Rest, ResumableHeaders, ResumeOptions, RotationSettings, RoutePlanner, ServerUpdate, Severity, Shoukaku, ShoukakuOptions, StateUpdatePartial, Structures, TimescaleSettings, Track, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, Utils, VoiceChannelOptions, WebSocketClosedEvent };
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var Shoukaku_exports = {};
__export(Shoukaku_exports, {
Connection: () => Connection,
Connector: () => Connector,
Connectors: () => libs_exports,
Constants: () => Constants_exports,
Filters: () => Filters,
Node: () => Node,
Player: () => Player,
Queue: () => Queue,
Rest: () => Rest,
Shoukaku: () => Shoukaku,
Utils: () => Utils_exports
});
module.exports = __toCommonJS(Shoukaku_exports);
// src/connectors/libs/index.ts
var libs_exports = {};
__export(libs_exports, {
DiscordJS: () => DiscordJS,
Eris: () => Eris,
OceanicJS: () => OceanicJS
});
// src/Constants.ts
var Constants_exports = {};
__export(Constants_exports, {
NodeDefaults: () => NodeDefaults,
OPCodes: () => OPCodes,
ShoukakuDefaults: () => ShoukakuDefaults,
State: () => State,
VoiceState: () => VoiceState
});
// package.json
var package_default = {
name: "shoukaku",
version: "3.4.0",
description: "A stable and updated wrapper around Lavalink",
main: "dist/index.js",
module: "dist/index.mjs",
types: "dist/index.d.ts",
exports: {
".": {
types: "./dist/index.d.ts",
import: "./dist/index.mjs",
require: "./dist/index.js"
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
},
scripts: {
build: "npm run build:ts && npm run build:docs",
"build:ts": "tsup --config tsup-config.json",
"build:docs": "typedoc --theme default --readme README.md --out docs/ --entryPointStrategy expand src/.",
prepare: "npm run build:ts"
},
keywords: [
"bot",
"music",
"lavalink",
"api",
"discord",
"lavalink.js",
"discord.js",
"lavalink-api",
"weeb-library"
],
engines: {
node: ">=16.0.0",
npm: ">=7.0.0"
},
author: "Saya",
license: "MIT",
repository: {
type: "git",
url: "https://github.com/Deivu/Shoukaku"
},
dependencies: {
"node-fetch": "^2.6.9",
ws: "^8.13.0"
},
devDependencies: {
"@augu/eslint-config": "^4.0.1",
"@types/node": "^18.16.1",
"@types/node-fetch": "^2.6.3",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
eslint: "^8.39.0",
tsup: "^6.7.0",
typedoc: "^0.24.6",
typescript: "^5.0.4"
}
};
// src/Constants.ts
var State = /* @__PURE__ */ ((State2) => {
State2[State2["CONNECTING"] = 0] = "CONNECTING";
State2[State2["CONNECTED"] = 1] = "CONNECTED";
State2[State2["DISCONNECTING"] = 2] = "DISCONNECTING";
State2[State2["DISCONNECTED"] = 3] = "DISCONNECTED";
return State2;
})(State || {});
var VoiceState = /* @__PURE__ */ ((VoiceState2) => {
VoiceState2[VoiceState2["SESSION_READY"] = 0] = "SESSION_READY";
VoiceState2[VoiceState2["SESSION_ID_MISSING"] = 1] = "SESSION_ID_MISSING";
VoiceState2[VoiceState2["SESSION_ENDPOINT_MISSING"] = 2] = "SESSION_ENDPOINT_MISSING";
return VoiceState2;
})(VoiceState || {});
var OPCodes = /* @__PURE__ */ ((OPCodes2) => {
OPCodes2["VOICE_UPDATE"] = "voiceUpdate";
OPCodes2["PLAY"] = "play";
OPCodes2["STOP"] = "stop";
OPCodes2["PAUSE"] = "pause";
OPCodes2["SEEK"] = "seek";
OPCodes2["VOLUME"] = "volume";
OPCodes2["FILTERS"] = "filters";
OPCodes2["DESTROY"] = "destroy";
OPCodes2["PLAYER_UPDATE"] = "playerUpdate";
OPCodes2["CONFIGURE_RESUMING"] = "configureResuming";
OPCodes2["STATS"] = "stats";
OPCodes2["EVENT"] = "event";
return OPCodes2;
})(OPCodes || {});
var ShoukakuDefaults = {
resume: false,
resumeKey: `Shoukaku@${package_default.version}(${package_default.repository.url})`,
resumeTimeout: 3e4,
resumeByLibrary: false,
alwaysSendResumeKey: false,
reconnectTries: 3,
reconnectInterval: 5e3,
restTimeout: 6e4,
moveOnDisconnect: false,
userAgent: `${package_default.name}bot/${package_default.version} (${package_default.repository.url})`,
structures: {}
};
var NodeDefaults = {
name: "Default",
url: "",
auth: "",
secure: false,
group: void 0
};
// src/Utils.ts
var Utils_exports = {};
__export(Utils_exports, {
mergeDefault: () => mergeDefault,
wait: () => wait
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
function mergeDefault(def, given) {
if (!given)
return def;
const defaultKeys = Object.keys(def);
for (const key in given) {
if (defaultKeys.includes(key))
continue;
delete given[key];
}
for (const key of defaultKeys) {
if (def[key] === null || typeof def[key] === "string" && def[key].length === 0) {
if (!given[key])
throw new Error(`${String(key)} was not found from the given options.`);
}
if (given[key] === null || given[key] === void 0)
given[key] = def[key];
}
return given;
}
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// src/connectors/Connector.ts
var Connector = class {
constructor(client) {
this.client = client;
this.manager = null;
}
set(manager) {
this.manager = manager;
return this;
}
ready(nodes) {
this.manager.id = this.getId();
this.manager.emit("debug", "Manager", `[Manager] : Connecting ${nodes.length} nodes`);
for (const node of nodes)
this.manager.addNode(mergeDefault(NodeDefaults, node));
}
raw(packet) {
if (!["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(packet.t))
return;
for (const node of this.manager.nodes.values())
node.discordRaw(packet);
}
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
// src/connectors/libs/DiscordJS.ts
var DiscordJS = class extends Connector {
// sendPacket is where your library send packets to Discord Gateway
sendPacket(shardId, payload, important) {
return this.client.ws.shards.get(shardId)?.send(payload, important);
}
// getId is a getter where the lib stores the client user (the one logged in as a bot) id
getId() {
return this.client.user.id;
}
// Listen attaches the event listener to the library you are using
listen(nodes) {
this.client.once("ready", () => this.ready(nodes));
this.client.on("raw", (packet) => this.raw(packet));
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Utils = exports.Constants = exports.Connectors = void 0;
exports.Connectors = __importStar(require("./src/connectors/libs"));
exports.Constants = __importStar(require("./src/Constants"));
exports.Utils = __importStar(require("./src/Utils"));
__exportStar(require("./src/connectors/Connector"), exports);
__exportStar(require("./src/guild/Connection"), exports);
__exportStar(require("./src/guild/Player"), exports);
__exportStar(require("./src/node/Node"), exports);
__exportStar(require("./src/node/Queue"), exports);
__exportStar(require("./src/node/Rest"), exports);
__exportStar(require("./src/Shoukaku"), exports);
// src/connectors/libs/Eris.ts
var Eris = class extends Connector {
// sendPacket is where your library send packets to Discord Gateway
sendPacket(shardId, payload, important) {
return this.client.shards.get(shardId)?.sendWS(payload.op, payload.d, important);
}
// getId is a getter where the lib stores the client user (the one logged in as a bot) id
getId() {
return this.client.user.id;
}
// Listen attaches the event listener to the library you are using
listen(nodes) {
this.client.once("ready", () => this.ready(nodes));
this.client.on("rawWS", (packet) => this.raw(packet));
}
};
// src/connectors/libs/OceanicJS.ts
var OceanicJS = class extends Connector {
// sendPacket is where your library send packets to Discord Gateway
sendPacket(shardId, payload, important) {
return this.client.shards.get(shardId)?.send(payload.op, payload.d, important);
}
// getId is a getter where the lib stores the client user (the one logged in as a bot) id
getId() {
return this.client.user.id;
}
// Listen attaches the event listener to the library you are using
listen(nodes) {
this.client.once("ready", () => this.ready(nodes));
this.client.on("packet", (packet) => this.raw(packet));
}
};
// src/guild/Connection.ts
var import_events = require("events");
var Connection = class extends import_events.EventEmitter {
/**
* @param player Shoukaku Player class
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
*/
constructor(player, options) {
super();
this.player = player;
this.guildId = options.guildId;
this.channelId = null;
this.shardId = options.shardId;
this.sessionId = null;
this.region = null;
this.muted = false;
this.deafened = false;
this.state = 3 /* DISCONNECTED */;
this.moved = false;
this.reconnecting = false;
this.serverUpdate = null;
}
/**
* Set the deafen status for the current bot user
* @param deaf Boolean value to indicate whether to deafen or undeafen
* @defaultValue false
*/
setDeaf(deaf = false) {
this.deafened = deaf;
this.send({ guild_id: this.guildId, channel_id: this.channelId, self_deaf: this.deafened, self_mute: this.muted }, false);
}
/**
* Set the mute status for the current bot user
* @param mute Boolean value to indicate whether to mute or unmute
* @defaultValue false
*/
setMute(mute = false) {
this.muted = mute;
this.send({ guild_id: this.guildId, channel_id: this.channelId, self_deaf: this.deafened, self_mute: this.muted }, false);
}
/**
* Disconnect the current bot user from the connected voice channel
*/
disconnect() {
if (this.state !== 3 /* DISCONNECTED */) {
this.state = 2 /* DISCONNECTING */;
this.send({ guild_id: this.guildId, channel_id: null, self_mute: false, self_deaf: false }, false);
}
this.player.node.players.delete(this.guildId);
this.player.clean();
this.destroyLavalinkPlayer();
this.state = 3 /* DISCONNECTED */;
this.player.node.emit("debug", this.player.node.name, `[Voice] -> [Node] & [Discord] : Link & Player Destroyed | Guild: ${this.guildId}`);
}
/**
* Connect the current bot user to a voice channel
*
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Unused parameter
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen or undeafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute or unmute the current bot user
*/
async connect(options) {
let { guildId, channelId, deaf, mute } = options;
if (typeof deaf === void 0)
deaf = true;
if (typeof mute === void 0)
mute = false;
this.state = 0 /* CONNECTING */;
this.send({ guild_id: guildId, channel_id: channelId, self_deaf: deaf, self_mute: mute }, false);
this.player.node.emit("debug", this.player.node.name, `[Voice] -> [Discord] : Requesting Connection | Guild: ${this.guildId}`);
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15e3);
try {
const [status] = await (0, import_events.once)(this, "connectionUpdate", { signal: controller.signal });
if (status !== 0 /* SESSION_READY */) {
if (status === 1 /* SESSION_ID_MISSING */)
throw new Error("The voice connection is not established due to missing session id");
else
throw new Error("The voice connection is not established due to missing connection endpoint");
}
this.state = 1 /* CONNECTED */;
} catch (error) {
this.player.node.emit("debug", this.player.node.name, `[Voice] </- [Discord] : Request Connection Failed | Guild: ${this.guildId}`);
if (error.name === "AbortError")
throw new Error("The voice connection is not established in 15 seconds");
throw error;
} finally {
clearTimeout(timeout);
}
}
/**
* Update Session ID, Channel ID, Deafen status and Mute status of this instance
*
* @param options.session_id ID of this session
* @param options.channel_id ID of currently connected voice channel
* @param options.self_deaf Boolean that indicates if the current bot user is deafened or not
* @param options.self_mute Boolean that indicates if the current bot user is muted or not
* @internal
*/
setStateUpdate(options) {
const { session_id, channel_id, self_deaf, self_mute } = options;
if (this.channelId && (channel_id && this.channelId !== channel_id)) {
this.moved = true;
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : Channel Moved | Old Channel: ${this.channelId} Guild: ${this.guildId}`);
}
this.channelId = channel_id || this.channelId;
if (!channel_id) {
this.state = 3 /* DISCONNECTED */;
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : Channel Disconnected | Guild: ${this.guildId}`);
}
this.deafened = self_deaf;
this.muted = self_mute;
this.sessionId = session_id || null;
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : State Update Received | Channel: ${this.channelId} Session ID: ${session_id} Guild: ${this.guildId}`);
}
/**
* Send voiceUpdate event to Lavalink and also cache the serverUpdate event from Discord
* @internal
*/
setServerUpdate(data) {
if (!data.endpoint) {
this.emit("connectionUpdate", 2 /* SESSION_ENDPOINT_MISSING */);
return;
}
if (!this.sessionId) {
this.emit("connectionUpdate", 1 /* SESSION_ID_MISSING */);
return;
}
if (this.region && !data.endpoint.startsWith(this.region)) {
this.moved = true;
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : Voice Region Moved | Old Region: ${this.region} Guild: ${this.guildId}`);
}
this.region = data.endpoint.split(".").shift()?.replace(/[0-9]/g, "") || null;
this.serverUpdate = data;
this.player.node.queue.add({ op: "voiceUpdate" /* VOICE_UPDATE */, guildId: this.guildId, sessionId: this.sessionId, event: this.serverUpdate });
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : Server Update, Voice Update Sent | Server: ${this.region} Guild: ${this.guildId}`);
this.emit("connectionUpdate", 0 /* SESSION_READY */);
}
/**
* Send voiceUpdate to Lavalink again
* @internal
*/
resendServerUpdate() {
if (!this.serverUpdate)
return;
this.player.node.queue.add({ op: "voiceUpdate" /* VOICE_UPDATE */, guildId: this.guildId, sessionId: this.sessionId, event: this.serverUpdate });
this.player.node.emit("debug", this.player.node.name, `[Voice] <- [Discord] : Server Update, Voice Update Resent! | Server: ${this.region} Guild: ${this.guildId}`);
}
/**
* Destroy the current Lavalink player
*/
destroyLavalinkPlayer() {
this.player.node.queue.add({ op: "destroy" /* DESTROY */, guildId: this.guildId });
this.player.node.emit("debug", this.player.node.name, `[Voice] -> [Discord] : Destroyed the player on Lavalink | Server: ${this.region} Guild: ${this.guildId}`);
}
/**
* Send data to Discord
* @param data The data to send
* @param important Whether to prioritize sending this packet in the queue
* @internal
*/
send(data, important = false) {
this.player.node.manager.connector.sendPacket(this.shardId, { op: 4, d: data }, important);
}
};
// src/guild/Player.ts
var import_events2 = require("events");
var Filters = class {
/**
* Options to initialize this filters instance with
* @param options.volume The volume to play audio at as a decimal
* @param options.equalizer An array of objects that conforms to the Bands type that define volumes at different frequencies
* @param options.karaoke An object that conforms to the KaraokeSettings type that defines a range of frequencies to mute
* @param options.timescale An object that conforms to the TimescaleSettings type that defines the time signature to play the audio at
* @param options.tremolo An object that conforms to the FreqSettings type that defines an ocillation in volume
* @param options.vibrato An object that conforms to the FreqSettings type that defines an ocillation in pitch
* @param options.rotation An object that conforms to the RotationSettings type that defines the frequency of audio rotating round the listener
* @param options.distortion An object that conforms to DistortionSettings that defines distortions in the audio
* @param options.channelMix An object that conforms to ChannelMixSettings that defines how much the left and right channels affect each other (setting all factors to 0.5 causes both channels to get the same audio)
* @param options.lowPass An object that conforms to LowPassSettings that defines the amount of suppression on higher frequencies
*/
constructor(options = {}) {
this.volume = options.volume ?? 1;
this.equalizer = options.equalizer || [];
this.karaoke = options.karaoke || null;
this.timescale = options.timescale || null;
this.tremolo = options.tremolo || null;
this.vibrato = options.vibrato || null;
this.rotation = options.rotation || null;
this.distortion = options.distortion || null;
this.channelMix = options.channelMix || null;
this.lowPass = options.lowPass || null;
}
};
var Player = class extends import_events2.EventEmitter {
/**
* @param node An instance of Node (Lavalink API wrapper)
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
*/
constructor(node, options) {
super();
this.node = node;
this.connection = new Connection(this, options);
this.track = null;
this.paused = false;
this.position = 0;
this.ping = 0;
this.filters = new Filters();
}
/**
* Move player to another node
* @param name Name of node to move to
* @returns The current player instance
*/
move(name) {
const node = this.node.manager.nodes.get(name);
if (!node || node.name === this.node.name)
return this;
if (node.state !== 1 /* CONNECTED */)
throw new Error("The node you specified is not ready");
this.connection.destroyLavalinkPlayer();
this.node.players.delete(this.connection.guildId);
this.node = node;
this.node.players.set(this.connection.guildId, this);
this.connection.resendServerUpdate();
this.resume();
return this;
}
/**
* Play a new track
* @param playable Options for playing this track
* @returns The current player instance
*/
playTrack(playable) {
const payload = {
op: "play" /* PLAY */,
guildId: this.connection.guildId,
track: playable.track,
noReplace: playable.options?.noReplace ?? true,
pause: playable.options?.pause ?? false
};
if (playable.options?.startTime)
payload.startTime = playable.options.startTime;
if (playable.options?.endTime)
payload.endTime = playable.options.startTime;
this.node.queue.add(payload);
this.track = playable.track;
this.paused = playable.options?.pause ?? false;
this.position = 0;
return this;
}
/**
* Stop the currently playing track
* @returns The current player instance
*/
stopTrack() {
this.position = 0;
this.node.queue.add({
op: "stop" /* STOP */,
guildId: this.connection.guildId
});
return this;
}
/**
* Pause or unpause the currently playing track
* @param pause Boolean value to specify whether to pause or unpause the current bot user
* @returns The current player instance
*/
setPaused(pause = true) {
this.node.queue.add({
op: "pause" /* PAUSE */,
guildId: this.connection.guildId,
pause
});
this.paused = pause;
return this;
}
/**
* Seek to a specific time in the currently playing track
* @param position Position to seek to in milliseconds
* @returns The current player instance
*/
seekTo(position) {
this.node.queue.add({
op: "seek" /* SEEK */,
guildId: this.connection.guildId,
position
});
return this;
}
/**
* Change the volume of the currently playing track
* @param volume Target volume as a decimal
* @returns The current player instance
*/
setVolume(volume) {
volume = Math.min(5, Math.max(0, volume));
this.filters.volume = volume;
this.updateFilters();
return this;
}
/**
* Change the equalizer settings applied to the currently playing track
* @param bands An array of objects that conforms to the Bands type that define volumes at different frequencies
* @returns The current player instance
*/
setEqualizer(bands) {
this.filters.equalizer = bands;
this.updateFilters();
return this;
}
/**
* Change the karaoke settings applied to the currently playing track
* @param karaoke An object that conforms to the KaraokeSettings type that defines a range of frequencies to mute
* @returns The current player instance
*/
setKaraoke(karaoke) {
this.filters.karaoke = karaoke || null;
this.updateFilters();
return this;
}
/**
* Change the timescale settings applied to the currently playing track
* @param timescale An object that conforms to the TimescaleSettings type that defines the time signature to play the audio at
* @returns The current player instance
*/
setTimescale(timescale) {
this.filters.timescale = timescale || null;
this.updateFilters();
return this;
}
/**
* Change the tremolo settings applied to the currently playing track
* @param tremolo An object that conforms to the FreqSettings type that defines an ocillation in volume
* @returns The current player instance
*/
setTremolo(tremolo) {
this.filters.tremolo = tremolo || null;
this.updateFilters();
return this;
}
/**
* Change the vibrato settings applied to the currently playing track
* @param vibrato An object that conforms to the FreqSettings type that defines an ocillation in pitch
* @returns The current player instance
*/
setVibrato(vibrato) {
this.filters.vibrato = vibrato || null;
this.updateFilters();
return this;
}
/**
* Change the rotation settings applied to the currently playing track
* @param rotation An object that conforms to the RotationSettings type that defines the frequency of audio rotating round the listener
* @returns The current player instance
*/
setRotation(rotation) {
this.filters.rotation = rotation || null;
this.updateFilters();
return this;
}
/**
* Change the distortion settings applied to the currently playing track
* @param distortion An object that conforms to DistortionSettings that defines distortions in the audio
* @returns The current player instance
*/
setDistortion(distortion) {
this.filters.distortion = distortion || null;
this.updateFilters();
return this;
}
/**
* Change the channel mix settings applied to the currently playing track
* @param mix An object that conforms to ChannelMixSettings that defines how much the left and right channels affect each other (setting all factors to 0.5 causes both channels to get the same audio)
* @returns The current player instance
*/
setChannelMix(mix) {
this.filters.channelMix = mix || null;
this.updateFilters();
return this;
}
/**
* Change the low pass settings applied to the currently playing track
* @param pass An object that conforms to LowPassSettings that defines the amount of suppression on higher frequencies
* @returns The current player instance
*/
setLowPass(pass) {
this.filters.lowPass = pass || null;
this.updateFilters();
return this;
}
/**
* Change the all filter settings applied to the currently playing track
* @param options An object that conforms to FilterOptions that defines all filters to apply/modify
* @returns The current player instance
*/
setFilters(options) {
this.filters = new Filters(options);
this.updateFilters();
return this;
}
/**
* Clear all filters applied to the currently playing track
* @returns The current player instance
*/
clearFilters() {
this.filters = new Filters();
this.node.queue.add({
op: "filters" /* FILTERS */,
guildId: this.connection.guildId
});
return this;
}
/**
* Resume the current track
* @param options An object that conforms to ResumeOptions that specify behavior on resuming
* @returns The current player instance
*/
resume(options = {}) {
this.updateFilters();
if (this.track) {
options = mergeDefault({ startTime: this.position, pause: this.paused }, options);
this.playTrack({ track: this.track, options });
}
this.emit("resumed");
return this;
}
/**
* Update all filters via the filters operation
* @internal
*/
updateFilters() {
const { volume, equalizer, karaoke, timescale, tremolo, vibrato, rotation, distortion, channelMix, lowPass } = this.filters;
this.node.queue.add({
op: "filters" /* FILTERS */,
guildId: this.connection.guildId,
volume,
equalizer,
karaoke,
timescale,
tremolo,
vibrato,
rotation,
distortion,
channelMix,
lowPass
});
}
/**
* Remove all event listeners on this instance
* @internal
*/
clean() {
this.removeAllListeners();
this.reset();
}
/**
* Reset the track, position and filters on this instance to defaults
*/
reset() {
this.track = null;
this.position = 0;
this.filters = new Filters();
}
/**
* Handle JSON data recieved from Lavalink
* @param json JSON data from Lavalink
* @internal
*/
onLavalinkMessage(json) {
if (json.op === "playerUpdate" /* PLAYER_UPDATE */) {
this.position = json.state.position;
this.ping = json.state.ping ?? 0;
this.emit("update", json);
} else if (json.op === "event" /* EVENT */)
this.onPlayerEvent(json);
else {
this.node.emit("debug", this.node.name, `[Player] -> [Node] : Unknown Message OP ${json.op} | Guild: ${this.connection.guildId}`);
}
}
/**
* Handle player events recieved from Lavalink
* @param json JSON data from Lavalink
* @internal
*/
onPlayerEvent(json) {
switch (json.type) {
case "TrackStartEvent":
this.position = 0;
this.emit("start", json);
break;
case "TrackEndEvent":
this.emit("end", json);
break;
case "TrackStuckEvent":
this.emit("stuck", json);
break;
case "TrackExceptionEvent":
this.emit("exception", json);
break;
case "WebSocketClosedEvent":
if (!this.connection.reconnecting) {
if (!this.connection.moved)
this.emit("closed", json);
else
this.connection.moved = false;
}
break;
default:
this.node.emit(
"debug",
this.node.name,
`[Player] -> [Node] : Unknown Player Event Type ${json.type} | Guild: ${this.connection.guildId}`
);
}
}
};
// src/node/Node.ts
var import_events3 = require("events");
// src/node/Queue.ts
var import_ws = __toESM(require("ws"));
var Queue = class {
/**
* @param node An instance of Node
*/
constructor(node) {
this.node = node;
this.pending = [];
this.flushes = 0;
}
/**
* Add data to queue
* @param data Message data
* @param important Priority status
* @internal
*/
add(data, important = false) {
if (data)
this.pending[important ? "unshift" : "push"](JSON.stringify(data));
this.process();
}
/**
* Clear the queue
* @internal
*/
clear() {
this.pending.length = 0;
}
/**
* Flush the ws connections in queue
* @param code Status code
* @param reason Reason for close
* @internal
*/
flush(code, reason) {
if (!this.pending.length || this.flushes > 10) {
this.flushes = 0;
this.clear();
this.node.ws?.close(code, reason);
return;
}
this.flushes++;
setTimeout(() => this.flush(code, reason), 1e3);
}
/**
* Process messages in the queue
* @internal
*/
process() {
if (!this.node.ws || this.node.ws.readyState !== import_ws.default.OPEN || !this.pending.length)
return;
while (this.pending.length) {
const message = this.pending.shift();
if (!message)
return;
this.node.ws.send(message);
}
}
};
// src/node/Rest.ts
var import_node_fetch = __toESM(require("node-fetch"));
var Rest = class {
/**
* @param node An instance of Node
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Weather to use secure protocols or not
* @param options.group Group of this node
*/
constructor(node, options) {
this.node = node;
this.url = `${options.secure ? "https" : "http"}://${options.url}`;
this.auth = options.auth;
}
/**
* Resolve a track
* @param identifier Track ID
* @returns A promise that resolves to a Lavalink response or void
*/
resolve(identifier) {
const options = {
endpoint: "/loadtracks",
options: { params: { identifier } }
};
return this.fetch(options);
}
/**
* Decode a track
* @param track Encoded track
* @returns Promise that resolves to a track or void
*/
decode(track) {
const options = {
endpoint: "/decodetrack",
options: { params: { track } }
};
return this.fetch(options);
}
/**
* Get routplanner status from Lavalink
* @returns Promise that resolves to a routeplanner response or void
* @internal
*/
getRoutePlannerStatus() {
const options = {
endpoint: "/routeplanner/status",
options: {}
};
return this.fetch(options);
}
/**
* Release blacklisted IP address into pool of IPs
* @param address IP address
* @internal
*/
unmarkFailedAddress(address) {
const options = {
endpoint: "/routeplanner/free/address",
options: {
method: "POST",
headers: { "Content-Type": "application/json" },
body: { address }
}
};
return this.fetch(options);
}
/**
* Make a request to Lavalink
* @param fetchOptions.endpoint Lavalink endpoint
* @param fetchOptions.options Options passed to fetch
* @internal
*/
async fetch(fetchOptions) {
const { endpoint, options } = fetchOptions;
let headers = {
"Authorization": this.auth,
"User-Agent": this.node.manager.options.userAgent
};
if (options.headers)
headers = { ...headers, ...options.headers };
const url = new URL(`${this.url}${endpoint}`);
if (options.params)
url.search = new URLSearchParams(options.params).toString();
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), this.node.manager.options.restTimeout || 15e3);
const request = await (0, import_node_fetch.default)(url.toString(), {
method: options.method?.toUpperCase() || "GET",
headers,
...["GET", "HEAD"].includes(options.method?.toUpperCase() || "GET") && options.body ? { body: JSON.stringify(options.body ?? {}) } : {},
// @ts-expect-error
signal: abortController.signal
}).finally(() => clearTimeout(timeout));
if (!request.ok)
throw new Error(`Rest request failed with response code: ${request.status}`);
return await request.json();
}
};
// src/node/Node.ts
var import_ws2 = __toESM(require("ws"));
var Node = class extends import_events3.EventEmitter {
/**
* @param manager Shoukaku instance
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Whether to use secure protocols or not
* @param options.group Group of this node
*/
constructor(manager, options) {
super();
this.manager = manager;
this.players = /* @__PURE__ */ new Map();
this.rest = new (this.manager.options.structures.rest || Rest)(this, options);
this.queue = new Queue(this);
this.name = options.name;
this.group = options.group;
this.url = `${options.secure ? "wss" : "ws"}://${options.url}`;
this.auth = options.auth;
this.reconnects = 0;
this.state = 3 /* DISCONNECTED */;
this.stats = null;
this.ws = null;
this.initialized = this.manager.options.alwaysSendResumeKey ?? false;
this.destroyed = false;
}
/**
* Penalties for load balancing
* @returns Penalty score
* @internal @readonly
*/
get penalties() {
let penalties = 0;
if (!this.stats)
return penalties;
penalties += this.stats.players;
penalties += Math.round(Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10);
if (this.stats.frameStats) {
penalties += this.stats.frameStats.deficit;
penalties += this.stats.frameStats.nulled * 2;
}
return penalties;
}
/**
* Connect to Lavalink
*/
connect() {
if (!this.manager.id)
throw new Error("Don't connect a node when the library is not yet ready");
if (this.destroyed)
throw new Error("You can't re-use the same instance of a node once disconnected, please re-add the node again");
const resume = this.initialized && (this.manager.options.resume && this.manager.options.resumeKey);
this.state = 0 /* CONNECTING */;
let headers;
if (resume) {
headers = {
"Client-Name": this.manager.options.userAgent,
"User-Agent": this.manager.options.userAgent,
"Authorization": this.auth,
"User-Id": this.manager.id,
"Resume-Key": this.manager.options.resumeKey
};
} else {
headers = {
"Client-Name": this.manager.options.userAgent,
"User-Agent": this.manager.options.userAgent,
"Authorization": this.auth,
"User-Id": this.manager.id
};
}
this.emit("debug", this.name, `[Socket] -> [${this.name}] : Connecting ${this.url}, Trying to resume? ${resume}`);
if (!this.initialized)
this.initialized = true;
this.ws = new import_ws2.default(this.url, { headers });
this.ws.once("upgrade", (response) => this.ws.once("open", () => this.open(response)));
this.ws.once("close", (...args) => this.close(...args));
this.ws.on("error", (error) => this.emit("error", this.name, error));
this.ws.on("message", (data) => this.message(data));
}
/**
* Disconnect from lavalink
* @param code Status code
* @param reason Reason for disconnect
*/
disconnect(code, reason) {
if (this.destroyed)
return;
this.destroyed = true;
this.state = 2 /* DISCONNECTING */;
this.clean();
this.queue.flush(code, reason);
}
/**
* Join a voice channel in a guild
* @param options.guildId Guild ID in which voice channel to connect to is located
* @param options.shardId Shard ID in which the guild exists
* @param options.channelId Channel ID of voice channel to connect to
* @param options.deaf Optional boolean value to specify whether to deafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute the current bot user
* @returns A promise that resolves to a player class
*/
async joinChannel(options) {
if (this.state !== 1 /* CONNECTED */)
throw new Error("This node is not yet ready");
let player = this.players.get(options.guildId);
if (player?.connection.state === 0 /* CONNECTING */)
throw new Error("Can't join this channel. This connection is connecting");
if (player?.connection.state === 1 /* CONNECTED */)
throw new Error("Can't join this channel. This connection is already connected");
if (player?.connection.reconnecting)
throw new Error("Can't join this channel. This connection is currently force-reconnecting");
try {
if (!player) {
if (this.manager.options.structures.player) {
player = new this.manager.options.structures.player(this, options);
} else {
player = new Player(this, options);
}
this.players.set(options.guildId, player);
}
await player.connection.connect(options);
return player;
} catch (error) {
this.players.delete(options.guildId);
throw error;
}
}
/**
* Disconnect from connected voice channel
* @param guildId ID of guild that contains voice channel
*/
leaveChannel(guildId) {
this.players.get(guildId)?.connection.disconnect();
}
/**
* Handle connection open event from Lavalink
* @param response Response from Lavalink
* @param reconnect Whether to reconnect on failure
* @internal
*/
open(response) {
const resumed = response.headers["session-resumed"] === "true";
this.queue.add();
if (this.manager.options.resume && this.manager.options.resumeKey) {
this.queue.add({
op: "configureResuming" /* CONFIGURE_RESUMING */,
key: this.manager.options.resumeKey,
timeout: this.manager.options.resumeTimeout
});
this.emit("debug", this.name, `[Socket] -> [${this.name}] : Resuming configured on Lavalink`);
}
const resumeByLibrary = this.initialized && (this.manager.options.resumeByLibrary && this.players.size);
if (!resumed && resumeByLibrary) {
for (const player of [...this.players.values()]) {
player.connection.resendServerUpdate();
player.resume();
}
}
this.emit("debug", this.name, `[Socket] <-> [${this.name}] : Connection ready ${this.url} | Lavalink Resumed: ${resumed} | Shoukaku Resumed: ${resumeByLibrary}`);
this.reconnects = 0;
this.state = 1 /* CONNECTED */;
this.emit("ready", this.name, resumed || resumeByLibrary);
}
/**
* Handle message from Lavalink
* @param message JSON message
* @internal
*/
message(message) {
const json = JSON.parse(message);
if (!json)
return;
this.emit("raw", this.name, json);
if (json.op === "stats" /* STATS */) {
this.emit("debug", this.name, `[Socket] <- [${this.name}] : Node Status Update | Server Load: ${this.penalties}`);
this.stats = json;
return;
}
this.players.get(json.guildId)?.onLavalinkMessage(json);
}
/**
* Handle closed event from lavalink
* @param code Status close
* @param reason Reason for connection close
*/
close(code, reason) {
this.destroy();
this.emit("debug", this.name, `[Socket] <-/-> [${this.name}] : Connection Closed, Code: ${code || "Unknown Code"}`);
this.emit("close", this.name, code, reason);
if (this.destroyed || this.reconnects >= this.manager.options.reconnectTries)
this.clean();
else
this.reconnect();
}
/**
* Destroys the websocket connection
* @internal
*/
destroy() {
this.ws?.removeAllListeners();
this.ws?.close();
this.ws = null;
this.state = 3 /* DISCONNECTED */;
}
/**
* Clear message queue and move players to other nodes if possible
* @internal
*/
clean() {
const players = [...this.players.values()];
const move = this.manager.options.moveOnDisconnect && [...this.manager.nodes.values()].filter((node) => node.group === this.group).length > 1;
for (const player of players) {
if (!move) {
player.connection.disconnect();
continue;
}
const name = this.group ? [this.group] : "auto";
const node = this.manager.getNode(name);
if (!node) {
player.connection.disconnect();
continue;
}
player.move(node.name);
}
this.queue.clear();
this.manager.nodes.delete(this.name);
this.emit("disconnect", this.name, players, players.length > 0 && move);
}
/**
* Reconnect to Lavalink
* @internal
*/
reconnect() {
if (this.state !== 3 /* DISCONNECTED */)
this.destroy();
this.reconnects++;
this.emit("reconnecting", this.name, `[Socket] -> [${this.name}] : Reconnecting in ${this.manager.options.reconnectInterval}ms. ${this.manager.options.reconnectTries - this.reconnects} tries left`, this.reconnect, this.manager.options.reconnectInterval, this.manager.options.reconnectTries - this.reconnects);
this.emit("debug", this.name, `[Socket] -> [${this.name}] : Reconnecting in ${this.manager.options.reconnectInterval}ms. ${this.manager.options.reconnectTries - this.reconnects} tries left`);
setTimeout(() => this.connect(), this.manager.options.reconnectInterval);
}
/**
* Handle raw message from Discord
* @param packet Packet data
* @internal
*/
discordRaw(packet) {
const player = this.players.get(packet.d.guild_id);
if (!player)
return;
if (packet.t === "VOICE_SERVER_UPDATE") {
player.connection.setServerUpdate(packet.d);
return;
}
if (packet.d.user_id !== this.manager.id)
return;
player.connection.setStateUpdate(packet.d);
}
};
// src/Shoukaku.ts
var import_events4 = require("events");
var Shoukaku = class extends import_events4.EventEmitter {
/**
* @param connector A Discord library connector
* @param nodes An array that conforms to the NodeOption type that specifies nodes to connect to
* @param options.resume Whether to resume a connection on disconnect to Lavalink (Server Side) (Note: DOES NOT RESUME WHEN THE LAVALINK SERVER DIES)
* @param options.resumeKey Resume key for Lavalink
* @param options.resumeTimeout Time to wait before lavalink starts to destroy the players of the disconnected client
* @param options.resumeByLibrary Whether to resume the players by doing it in the library side (Client Side) (Note: TRIES TO RESUME REGARDLESS OF WHAT HAPPENED ON A LAVALINK SERVER)
* @param options.alwaysSendResumeKey Disables the first time initialization tracking of nodes, and just sends the resume key always (Note: Useful for people who save their players to redis and wants to resume sessions even at first boot)
* @param options.reconnectTries Number of times to try and reconnect to Lavalink before giving up
* @param options.reconnectInterval Timeout before trying to reconnect
* @param options.restTimeout Time to wait for a response from the Lavalink REST API before giving up
* @param options.moveOnDisconnect Whether to move players to a different Lavalink node when a node disconnects
* @param options.userAgent User Agent to use when making requests to Lavalink
* @param options.structures Custom structures for shoukaku to use
*/
constructor(connector, nodes, options = {}) {
super();
this.connector = connector.set(this);
this.options = mergeDefault(ShoukakuDefaults, options);
this.nodes = /* @__PURE__ */ new Map();
this.id = null;
this.connector.listen(nodes);
}
/**
* Get a list of players
* @returns A map of guild IDs and players
* @readonly
*/
get players() {
const players = /* @__PURE__ */ new Map();
for (const node of this.nodes.values()) {
for (const [id, player] of node.players)
players.set(id, player);
}
return players;
}
/**
* Add a Lavalink node to the pool of available nodes
* @param options.name Name of this node
* @param options.url URL of Lavalink
* @param options.auth Credentials to access Lavalnk
* @param options.secure Whether to use secure protocols or not
* @param options.group Group of this node
*/
addNode(options) {
const node = new Node(this, options);
node.on("debug", (...args) => this.emit("debug", ...args));
node.on("reconnecting", (...args) => this.emit("reconnecting", ...args));
node.on("error", (...args) => this.emit("error", ...args));
node.on("close", (...args) => this.emit("close", ...args));
node.on("ready", (...args) => this.emit("ready", ...args));
node.on("raw", (...args) => this.emit("raw", ...args));
node.on("disconnect", (...args) => this.emit("disconnect", ...args));
node.connect();
this.nodes.set(node.name, node);
}
/**
* Remove a Lavalink node from the pool of available nodes
* @param name Name of the node
* @param reason Reason of removing the node
*/
removeNode(name, reason = "Remove node executed") {
const node = this.nodes.get(name);
if (!node)
throw new Error("The node name you specified doesn't exist");
node.disconnect(1e3, reason);
node.removeAllListeners();
}
/**
* Select a Lavalink node from the pool of nodes
* @param name A specific node, an array of nodes, or the string `auto`
* @returns A Lavalink node or undefined
*/
getNode(name = "auto") {
if (!this.nodes.size)
throw new Error("No nodes available, please add a node first");
if (Array.isArray(name) || name === "auto")
return this.getIdeal(name);
const node = this.nodes.get(name);
if (!node)
throw new Error("The node name you specified is not one of my nodes");
if (node.state !== 1 /* CONNECTED */)
throw new Error("This node is not yet ready");
return node;
}
/**
* Get the Lavalink node the least penalty score
* @param group A group, an array of groups, or the string `auto`
* @returns A Lavalink node or undefined
* @internal
*/
getIdeal(group) {
const nodes = [...this.nodes.values()].filter((node) => node.state === 1 /* CONNECTED */);
if (group === "auto") {
return nodes.sort((a, b) => a.penalties - b.penalties).shift();
}
return nodes.filter((node) => node.group && group.includes(node.group)).sort((a, b) => a.penalties - b.penalties).shift();
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Connection,
Connector,
Connectors,
Constants,
Filters,
Node,
Player,
Queue,
Rest,
Shoukaku,
Utils
});
//# sourceMappingURL=index.js.map

32

package.json
{
"name": "shoukaku",
"version": "3.3.1",
"version": "3.4.0",
"description": "A stable and updated wrapper around Lavalink",

@@ -10,2 +10,3 @@ "main": "dist/index.js",

".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",

@@ -16,6 +17,6 @@ "require": "./dist/index.js"

"scripts": {
"build": "tsc && npm run build:esm && npm run build:docs",
"build:esm": "gen-esm-wrapper dist/index.js dist/index.mjs",
"build": "npm run build:ts && npm run build:docs",
"build:ts": "tsup --config tsup-config.json",
"build:docs": "typedoc --theme default --readme README.md --out docs/ --entryPointStrategy expand src/.",
"prepare": "tsc && npm run build:esm"
"prepare": "npm run build:ts"
},

@@ -44,18 +45,17 @@ "keywords": [

"dependencies": {
"node-fetch": "^2.6.7",
"ws": "^8.12.0"
"node-fetch": "^2.6.9",
"ws": "^8.13.0"
},
"devDependencies": {
"@augu/eslint-config": "^3.0.0",
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.2",
"@augu/eslint-config": "^4.0.1",
"@types/node": "^18.16.1",
"@types/node-fetch": "^2.6.3",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"eslint": "^8.23.0",
"gen-esm-wrapper": "^1.1.3",
"ts-node": "^10.9.1",
"typedoc": "^0.23.24",
"typescript": "^4.9.4"
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"eslint": "^8.39.0",
"tsup": "^6.7.0",
"typedoc": "^0.24.6",
"typescript": "^5.0.4"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc