lavalink-client 2.3.1 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type { DebugEvents } from "../Constants";
1
2
  import type { Player } from "../Player";
2
3
  import type { LavalinkNodeOptions } from "./Node";
3
4
  import type { DestroyReasonsType, PlayerJson } from "./Player";
@@ -100,6 +101,19 @@ export interface LavalinkManagerEvents {
100
101
  * @event Manager#trackError
101
102
  */
102
103
  "ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack | null, payload: SponsorBlockChaptersLoaded) => void;
104
+ /**
105
+ * Lavalink-Client Debug Event
106
+ * Emitted for several erros, and logs within lavalink-client, if managerOptions.advancedOptions.enableDebugEvents is true
107
+ * Useful for debugging the lavalink-client
108
+ *
109
+ * @event Manager#debug
110
+ */
111
+ "debug": (eventKey: DebugEvents, eventData: {
112
+ message: string;
113
+ state: "log" | "warn" | "error";
114
+ error?: Error | string;
115
+ functionLayer: string;
116
+ }) => void;
103
117
  }
104
118
  /**
105
119
  * The Bot client Options needed for the manager
@@ -131,6 +145,15 @@ export interface ManagerPlayerOptions {
131
145
  /** Instantly destroy player (overrides autoReconnect) | Don't provide == disable feature*/
132
146
  destroyPlayer?: boolean;
133
147
  };
148
+ /** Minimum time to play the song before autoPlayFunction is executed (prevents error spamming) Set to 0 to disable it @default 10000 */
149
+ minAutoPlayMs?: number;
150
+ /** Allows you to declare how many tracks are allowed to error/stuck within a time-frame before player is destroyed @default "{threshold: 35000, maxAmount: 3 }" */
151
+ maxErrorsPerTime?: {
152
+ /** The threshold time to count errors (recommended is 35s) */
153
+ threshold: number;
154
+ /** The max amount of errors within the threshold time which are allowed before destroying the player (when errors > maxAmount -> player.destroy()) */
155
+ maxAmount: number;
156
+ };
134
157
  onEmptyQueue?: {
135
158
  /** Get's executed onEmptyQueue -> You can do any track queue previous transformations, if you add a track to the queue -> it will play it, if not queueEnd will execute! */
136
159
  autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
@@ -166,6 +189,8 @@ export interface ManagerOptions {
166
189
  advancedOptions?: {
167
190
  /** Max duration for that the filter fix duration works (in ms) - default is 8mins */
168
191
  maxFilterFixDuration?: number;
192
+ /** Enable Debug event */
193
+ enableDebugEvents?: boolean;
169
194
  /** optional */
170
195
  debugOptions?: {
171
196
  /** For logging custom searches */
@@ -28,7 +28,7 @@ export interface QueueChangesWatcher {
28
28
  /** get a Value (MUST RETURN UNPARSED!) */
29
29
  tracksAdd: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
30
30
  /** Set a value inside a guildId (MUST BE UNPARSED) */
31
- tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
31
+ tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number | number[], oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
32
32
  /** Set a value inside a guildId (MUST BE UNPARSED) */
33
33
  shuffled: (guildId: string, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => void;
34
34
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.queueTrackEnd = exports.MiniMap = exports.ManagerUtils = exports.parseLavalinkConnUrl = exports.NodeSymbol = exports.QueueSymbol = exports.UnresolvedTrackSymbol = exports.TrackSymbol = void 0;
4
4
  const node_url_1 = require("node:url");
5
5
  const types_1 = require("node:util/types");
6
+ const Constants_1 = require("./Constants");
6
7
  const LavalinkManagerStatics_1 = require("./LavalinkManagerStatics");
7
8
  exports.TrackSymbol = Symbol("LC-Track");
8
9
  exports.UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
@@ -75,6 +76,14 @@ class ManagerUtils {
75
76
  return r;
76
77
  }
77
78
  catch (error) {
79
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
80
+ this.LavalinkManager?.emit("debug", Constants_1.DebugEvents.BuildTrackError, {
81
+ error: error,
82
+ functionLayer: "ManagerUtils > buildTrack()",
83
+ message: "Error while building track",
84
+ state: "error",
85
+ });
86
+ }
78
87
  throw new RangeError(`Argument "data" is not a valid track: ${error.message}`);
79
88
  }
80
89
  }
@@ -133,7 +142,14 @@ class ManagerUtils {
133
142
  : requester;
134
143
  }
135
144
  catch (e) {
136
- console.error("errored while transforming requester:", e);
145
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
146
+ this.LavalinkManager?.emit("debug", Constants_1.DebugEvents.TransformRequesterFunctionFailed, {
147
+ error: e,
148
+ functionLayer: "ManagerUtils > getTransformedRequester()",
149
+ message: "Your custom transformRequesterFunction failed to execute, please check your function for errors.",
150
+ state: "error",
151
+ });
152
+ }
137
153
  return requester;
138
154
  }
139
155
  }
@@ -199,62 +215,96 @@ class ManagerUtils {
199
215
  return typeof data === "object" && !("info" in data) && typeof data.title === "string";
200
216
  }
201
217
  async getClosestTrack(data, player) {
202
- return getClosestTrack(data, player);
218
+ try {
219
+ return getClosestTrack(data, player);
220
+ }
221
+ catch (e) {
222
+ if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
223
+ this.LavalinkManager?.emit("debug", Constants_1.DebugEvents.GetClosestTrackFailed, {
224
+ error: e,
225
+ functionLayer: "ManagerUtils > getClosestTrack()",
226
+ message: "Failed to resolve track because the getClosestTrack function failed.",
227
+ state: "error",
228
+ });
229
+ }
230
+ throw e;
231
+ }
203
232
  }
204
233
  validateQueryString(node, queryString, sourceString) {
205
234
  if (!node.info)
206
235
  throw new Error("No Lavalink Node was provided");
207
236
  if (!node.info.sourceManagers?.length)
208
237
  throw new Error("Lavalink Node, has no sourceManagers enabled");
238
+ if (!queryString.trim().length)
239
+ throw new Error(`Query string is empty, please provide a valid query string.`);
209
240
  if (sourceString === "speak" && queryString.length > 100)
210
- // checks for blacklisted links / domains / queries
211
- if (this.LavalinkManager.options?.linksBlacklist?.length > 0 && this.LavalinkManager.options?.linksBlacklist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
241
+ throw new Error(`Query is speak, which is limited to 100 characters.`);
242
+ // checks for blacklisted links / domains / queries
243
+ if (this.LavalinkManager.options?.linksBlacklist?.length > 0) {
244
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
245
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.ValidatingBlacklistLinks, {
246
+ state: "log",
247
+ message: `Validating Query against LavalinkManager.options.linksBlacklist, query: "${queryString}"`,
248
+ functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()",
249
+ });
250
+ }
251
+ if (this.LavalinkManager.options?.linksBlacklist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
212
252
  throw new Error(`Query string contains a link / word which is blacklisted.`);
213
253
  }
254
+ }
214
255
  if (!/^https?:\/\//.test(queryString))
215
256
  return;
216
257
  else if (this.LavalinkManager.options?.linksAllowed === false)
217
258
  throw new Error("Using links to make a request is not allowed.");
218
259
  // checks for if the query is whitelisted (should only work for links, so it skips the check for no link queries)
219
- if (this.LavalinkManager.options?.linksWhitelist?.length > 0 && !this.LavalinkManager.options?.linksWhitelist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
220
- throw new Error(`Query string contains a link / word which isn't whitelisted.`);
260
+ if (this.LavalinkManager.options?.linksWhitelist?.length > 0) {
261
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
262
+ this.LavalinkManager.emit("debug", Constants_1.DebugEvents.ValidatingWhitelistLinks, {
263
+ state: "log",
264
+ message: `Link was provided to the Query, validating against LavalinkManager.options.linksWhitelist, query: "${queryString}"`,
265
+ functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()",
266
+ });
267
+ }
268
+ if (!this.LavalinkManager.options?.linksWhitelist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
269
+ throw new Error(`Query string contains a link / word which isn't whitelisted.`);
270
+ }
221
271
  }
222
272
  // missing links: beam.pro local getyarn.io clypit pornhub reddit ocreamix soundgasm
223
273
  if ((LavalinkManagerStatics_1.SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || LavalinkManagerStatics_1.SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
224
- throw new Error("Lavalink Node has not 'youtube' enabled");
274
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'youtube' enabled");
225
275
  }
226
276
  if ((LavalinkManagerStatics_1.SourceLinksRegexes.SoundCloudMobileRegex.test(queryString) || LavalinkManagerStatics_1.SourceLinksRegexes.SoundCloudRegex.test(queryString)) && !node.info?.sourceManagers?.includes("soundcloud")) {
227
- throw new Error("Lavalink Node has not 'soundcloud' enabled");
277
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'soundcloud' enabled");
228
278
  }
229
279
  if (LavalinkManagerStatics_1.SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
230
- throw new Error("Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
280
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
231
281
  }
232
282
  if (LavalinkManagerStatics_1.SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
233
- throw new Error("Lavalink Node has not 'twitch' enabled");
283
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'twitch' enabled");
234
284
  }
235
285
  if (LavalinkManagerStatics_1.SourceLinksRegexes.vimeo.test(queryString) && !node.info?.sourceManagers?.includes("vimeo")) {
236
- throw new Error("Lavalink Node has not 'vimeo' enabled");
286
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'vimeo' enabled");
237
287
  }
238
288
  if (LavalinkManagerStatics_1.SourceLinksRegexes.tiktok.test(queryString) && !node.info?.sourceManagers?.includes("tiktok")) {
239
- throw new Error("Lavalink Node has not 'tiktok' enabled");
289
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tiktok' enabled");
240
290
  }
241
291
  if (LavalinkManagerStatics_1.SourceLinksRegexes.mixcloud.test(queryString) && !node.info?.sourceManagers?.includes("mixcloud")) {
242
- throw new Error("Lavalink Node has not 'mixcloud' enabled");
292
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'mixcloud' enabled");
243
293
  }
244
294
  if (LavalinkManagerStatics_1.SourceLinksRegexes.AllSpotifyRegex.test(queryString) && !node.info?.sourceManagers?.includes("spotify")) {
245
- throw new Error("Lavalink Node has not 'spotify' enabled");
295
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'spotify' enabled");
246
296
  }
247
297
  if (LavalinkManagerStatics_1.SourceLinksRegexes.appleMusic.test(queryString) && !node.info?.sourceManagers?.includes("applemusic")) {
248
- throw new Error("Lavalink Node has not 'applemusic' enabled");
298
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'applemusic' enabled");
249
299
  }
250
300
  if (LavalinkManagerStatics_1.SourceLinksRegexes.AllDeezerRegex.test(queryString) && !node.info?.sourceManagers?.includes("deezer")) {
251
- throw new Error("Lavalink Node has not 'deezer' enabled");
301
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'deezer' enabled");
252
302
  }
253
303
  if (LavalinkManagerStatics_1.SourceLinksRegexes.AllDeezerRegex.test(queryString) && node.info?.sourceManagers?.includes("deezer") && !node.info?.sourceManagers?.includes("http")) {
254
- throw new Error("Lavalink Node has not 'http' enabled, which is required to have 'deezer' to work");
304
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'http' enabled, which is required to have 'deezer' to work");
255
305
  }
256
306
  if (LavalinkManagerStatics_1.SourceLinksRegexes.musicYandex.test(queryString) && !node.info?.sourceManagers?.includes("yandexmusic")) {
257
- throw new Error("Lavalink Node has not 'yandexmusic' enabled");
307
+ throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'yandexmusic' enabled");
258
308
  }
259
309
  return;
260
310
  }
@@ -1,4 +1,45 @@
1
1
  import type { AudioOutputs, ChannelMixFilter, EQBand } from "./Types/Filters";
2
+ export declare enum DebugEvents {
3
+ SetSponsorBlock = "SetSponsorBlock",
4
+ DeleteSponsorBlock = "DeleteSponsorBlock",
5
+ TrackEndReplaced = "TrackEndReplaced",
6
+ AutoplayNoSongsAdded = "AutoplayNoSongsAdded",
7
+ AutoplayThresholdSpamLimiter = "AutoplayThresholdSpamLimiter",
8
+ TriggerQueueEmptyInterval = "TriggerQueueEmptyInterval",
9
+ QueueEnded = "QueueEnded",
10
+ TrackStartNewSongsOnly = "TrackStartNewSongsOnly",
11
+ TrackStartNoTrack = "TrackStartNoTrack",
12
+ ResumingFetchingError = "ResumingFetchingError",
13
+ PlayerUpdateNoPlayer = "PlayerUpdateNoPlayer",
14
+ PlayerUpdateFilterFixApply = "PlayerUpdateFilterFixApply",
15
+ PlayerUpdateSuccess = "PlayerUpdateSuccess",
16
+ HeartBeatTriggered = "HeartBeatTriggered",
17
+ NoSocketOnDestroy = "NoSocketOnDestroy",
18
+ SocketTerminateHeartBeatTimeout = "SocketTerminateHeartBeatTimeout",
19
+ TryingConnectWhileConnected = "TryingConnectWhileConnected",
20
+ LavaSearchNothingFound = "LavaSearchNothingFound",
21
+ SearchNothingFound = "SearchNothingFound",
22
+ ValidatingBlacklistLinks = "ValidatingBlacklistLinks",
23
+ ValidatingWhitelistLinks = "ValidatingWhitelistLinks",
24
+ TrackErrorMaxTracksErroredPerTime = "TrackErrorMaxTracksErroredPerTime",
25
+ TrackStuckMaxTracksErroredPerTime = "TrackStuckMaxTracksErroredPerTime",
26
+ PlayerDestroyingSomewhereElse = "PlayerDestroyingSomewhereElse",
27
+ PlayerCreateNodeNotFound = "PlayerCreateNodeNotFound",
28
+ PlayerPlayQueueEmptyTimeoutClear = "PlayerPlayQueueEmptyTimeoutClear",
29
+ PlayerPlayWithTrackReplace = "PlayerPlayWithTrackReplace",
30
+ PlayerPlayUnresolvedTrack = "PlayerPlayUnresolvedTrack",
31
+ PlayerPlayUnresolvedTrackFailed = "PlayerPlayUnresolvedTrackFailed",
32
+ PlayerVolumeAsFilter = "PlayerVolumeAsFilter",
33
+ BandcampSearchLokalEngine = "BandcampSearchLokalEngine",
34
+ PlayerChangeNode = "PlayerChangeNode",
35
+ BuildTrackError = "BuildTrackError",
36
+ TransformRequesterFunctionFailed = "TransformRequesterFunctionFailed",
37
+ GetClosestTrackFailed = "GetClosestTrackFailed",
38
+ PlayerDeleteInsteadOfDestroy = "PlayerDeleteInsteadOfDestroy",
39
+ FailedToConnectToNodes = "FailedToConnectToNodes",
40
+ NoAudioDebug = "NoAudioDebug",
41
+ PlayerAutoReconnect = "PlayerAutoReconnect"
42
+ }
2
43
  export declare enum DestroyReasons {
3
44
  QueueEmpty = "QueueEmpty",
4
45
  NodeDestroy = "NodeDestroy",
@@ -9,7 +50,9 @@ export declare enum DestroyReasons {
9
50
  PlayerReconnectFail = "PlayerReconnectFail",
10
51
  ChannelDeleted = "ChannelDeleted",
11
52
  DisconnectAllNodes = "DisconnectAllNodes",
12
- ReconnectAllNodes = "ReconnectAllNodes"
53
+ ReconnectAllNodes = "ReconnectAllNodes",
54
+ TrackErrorMaxTracksErroredPerTime = "TrackErrorMaxTracksErroredPerTime",
55
+ TrackStuckMaxTracksErroredPerTime = "TrackStuckMaxTracksErroredPerTime"
13
56
  }
14
57
  export declare const validSponsorBlocks: string[];
15
58
  /** The audio Outputs Data map declaration */
@@ -1,3 +1,45 @@
1
+ export var DebugEvents;
2
+ (function (DebugEvents) {
3
+ DebugEvents["SetSponsorBlock"] = "SetSponsorBlock";
4
+ DebugEvents["DeleteSponsorBlock"] = "DeleteSponsorBlock";
5
+ DebugEvents["TrackEndReplaced"] = "TrackEndReplaced";
6
+ DebugEvents["AutoplayNoSongsAdded"] = "AutoplayNoSongsAdded";
7
+ DebugEvents["AutoplayThresholdSpamLimiter"] = "AutoplayThresholdSpamLimiter";
8
+ DebugEvents["TriggerQueueEmptyInterval"] = "TriggerQueueEmptyInterval";
9
+ DebugEvents["QueueEnded"] = "QueueEnded";
10
+ DebugEvents["TrackStartNewSongsOnly"] = "TrackStartNewSongsOnly";
11
+ DebugEvents["TrackStartNoTrack"] = "TrackStartNoTrack";
12
+ DebugEvents["ResumingFetchingError"] = "ResumingFetchingError";
13
+ DebugEvents["PlayerUpdateNoPlayer"] = "PlayerUpdateNoPlayer";
14
+ DebugEvents["PlayerUpdateFilterFixApply"] = "PlayerUpdateFilterFixApply";
15
+ DebugEvents["PlayerUpdateSuccess"] = "PlayerUpdateSuccess";
16
+ DebugEvents["HeartBeatTriggered"] = "HeartBeatTriggered";
17
+ DebugEvents["NoSocketOnDestroy"] = "NoSocketOnDestroy";
18
+ DebugEvents["SocketTerminateHeartBeatTimeout"] = "SocketTerminateHeartBeatTimeout";
19
+ DebugEvents["TryingConnectWhileConnected"] = "TryingConnectWhileConnected";
20
+ DebugEvents["LavaSearchNothingFound"] = "LavaSearchNothingFound";
21
+ DebugEvents["SearchNothingFound"] = "SearchNothingFound";
22
+ DebugEvents["ValidatingBlacklistLinks"] = "ValidatingBlacklistLinks";
23
+ DebugEvents["ValidatingWhitelistLinks"] = "ValidatingWhitelistLinks";
24
+ DebugEvents["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
25
+ DebugEvents["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
26
+ DebugEvents["PlayerDestroyingSomewhereElse"] = "PlayerDestroyingSomewhereElse";
27
+ DebugEvents["PlayerCreateNodeNotFound"] = "PlayerCreateNodeNotFound";
28
+ DebugEvents["PlayerPlayQueueEmptyTimeoutClear"] = "PlayerPlayQueueEmptyTimeoutClear";
29
+ DebugEvents["PlayerPlayWithTrackReplace"] = "PlayerPlayWithTrackReplace";
30
+ DebugEvents["PlayerPlayUnresolvedTrack"] = "PlayerPlayUnresolvedTrack";
31
+ DebugEvents["PlayerPlayUnresolvedTrackFailed"] = "PlayerPlayUnresolvedTrackFailed";
32
+ DebugEvents["PlayerVolumeAsFilter"] = "PlayerVolumeAsFilter";
33
+ DebugEvents["BandcampSearchLokalEngine"] = "BandcampSearchLokalEngine";
34
+ DebugEvents["PlayerChangeNode"] = "PlayerChangeNode";
35
+ DebugEvents["BuildTrackError"] = "BuildTrackError";
36
+ DebugEvents["TransformRequesterFunctionFailed"] = "TransformRequesterFunctionFailed";
37
+ DebugEvents["GetClosestTrackFailed"] = "GetClosestTrackFailed";
38
+ DebugEvents["PlayerDeleteInsteadOfDestroy"] = "PlayerDeleteInsteadOfDestroy";
39
+ DebugEvents["FailedToConnectToNodes"] = "FailedToConnectToNodes";
40
+ DebugEvents["NoAudioDebug"] = "NoAudioDebug";
41
+ DebugEvents["PlayerAutoReconnect"] = "PlayerAutoReconnect";
42
+ })(DebugEvents || (DebugEvents = {}));
1
43
  export var DestroyReasons;
2
44
  (function (DestroyReasons) {
3
45
  DestroyReasons["QueueEmpty"] = "QueueEmpty";
@@ -10,6 +52,8 @@ export var DestroyReasons;
10
52
  DestroyReasons["ChannelDeleted"] = "ChannelDeleted";
11
53
  DestroyReasons["DisconnectAllNodes"] = "DisconnectAllNodes";
12
54
  DestroyReasons["ReconnectAllNodes"] = "ReconnectAllNodes";
55
+ DestroyReasons["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
56
+ DestroyReasons["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
13
57
  })(DestroyReasons || (DestroyReasons = {}));
14
58
  ;
15
59
  export const validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from "events";
2
- import { DestroyReasons } from "./Constants";
2
+ import { DebugEvents, DestroyReasons } from "./Constants";
3
3
  import { NodeManager } from "./NodeManager";
4
4
  import { Player } from "./Player";
5
5
  import { DefaultQueueStore } from "./Queue";
@@ -89,9 +89,15 @@ export class LavalinkManager extends EventEmitter {
89
89
  volumeDecrementer: options?.playerOptions?.volumeDecrementer ?? 1,
90
90
  requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
91
91
  useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
92
+ minAutoPlayMs: options?.playerOptions?.minAutoPlayMs ?? 10000,
93
+ maxErrorsPerTime: {
94
+ threshold: options?.playerOptions?.maxErrorsPerTime?.threshold ?? 35000,
95
+ maxAmount: options?.playerOptions?.maxErrorsPerTime?.maxAmount ?? 3
96
+ }
92
97
  },
93
98
  linksWhitelist: options?.linksWhitelist ?? [],
94
99
  linksBlacklist: options?.linksBlacklist ?? [],
100
+ linksAllowed: options?.linksAllowed ?? true,
95
101
  autoSkip: options?.autoSkip ?? true,
96
102
  autoSkipOnResolveError: options?.autoSkipOnResolveError ?? true,
97
103
  emitNewSongsOnly: options?.emitNewSongsOnly ?? false,
@@ -101,6 +107,7 @@ export class LavalinkManager extends EventEmitter {
101
107
  queueStore: options?.queueOptions?.queueStore ?? new DefaultQueueStore(),
102
108
  },
103
109
  advancedOptions: {
110
+ enableDebugEvents: options?.advancedOptions?.enableDebugEvents ?? false,
104
111
  maxFilterFixDuration: options?.advancedOptions?.maxFilterFixDuration ?? 600000,
105
112
  debugOptions: {
106
113
  logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
@@ -304,8 +311,13 @@ export class LavalinkManager extends EventEmitter {
304
311
  if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
305
312
  if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
306
313
  throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
307
- else
308
- console.error("Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player", oldPlayer.toJSON?.());
314
+ else if (this.options?.advancedOptions?.enableDebugEvents) {
315
+ this.emit("debug", DebugEvents.PlayerDeleteInsteadOfDestroy, {
316
+ state: "warn",
317
+ message: "Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player",
318
+ functionLayer: "LavalinkManager > deletePlayer()",
319
+ });
320
+ }
309
321
  }
310
322
  return this.players.delete(guildId);
311
323
  }
@@ -358,8 +370,13 @@ export class LavalinkManager extends EventEmitter {
358
370
  }
359
371
  if (success > 0)
360
372
  this.initiated = true;
361
- else
362
- console.error("Could not connect to at least 1 Node");
373
+ else if (this.options?.advancedOptions?.enableDebugEvents) {
374
+ this.emit("debug", DebugEvents.FailedToConnectToNodes, {
375
+ state: "error",
376
+ message: "Failed to connect to at least 1 Node",
377
+ functionLayer: "LavalinkManager > init()",
378
+ });
379
+ }
363
380
  return this;
364
381
  }
365
382
  /**
@@ -379,11 +396,25 @@ export class LavalinkManager extends EventEmitter {
379
396
  */
380
397
  async sendRawData(data) {
381
398
  if (!this.initiated) {
399
+ if (this.options?.advancedOptions?.enableDebugEvents) {
400
+ this.emit("debug", DebugEvents.NoAudioDebug, {
401
+ state: "log",
402
+ message: "Manager is not initated yet",
403
+ functionLayer: "LavalinkManager > sendRawData()",
404
+ });
405
+ }
382
406
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
383
407
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
384
408
  return;
385
409
  }
386
410
  if (!("t" in data)) {
411
+ if (this.options?.advancedOptions?.enableDebugEvents) {
412
+ this.emit("debug", DebugEvents.NoAudioDebug, {
413
+ state: "error",
414
+ message: "No 't' in payload-data of the raw event:",
415
+ functionLayer: "LavalinkManager > sendRawData()",
416
+ });
417
+ }
387
418
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
388
419
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
389
420
  return;
@@ -401,22 +432,50 @@ export class LavalinkManager extends EventEmitter {
401
432
  if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
402
433
  const update = ("d" in data ? data.d : data);
403
434
  if (!update) {
435
+ if (this.options?.advancedOptions?.enableDebugEvents) {
436
+ this.emit("debug", DebugEvents.NoAudioDebug, {
437
+ state: "warn",
438
+ message: `No Update data found in payload :: ${JSON.stringify(data, null, 2)}`,
439
+ functionLayer: "LavalinkManager > sendRawData()",
440
+ });
441
+ }
404
442
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
405
443
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
406
444
  return;
407
445
  }
408
446
  if (!("token" in update) && !("session_id" in update)) {
447
+ if (this.options?.advancedOptions?.enableDebugEvents) {
448
+ this.emit("debug", DebugEvents.NoAudioDebug, {
449
+ state: "error",
450
+ message: `No 'token' nor 'session_id' found in payload :: ${JSON.stringify(data, null, 2)}`,
451
+ functionLayer: "LavalinkManager > sendRawData()",
452
+ });
453
+ }
409
454
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
410
455
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
411
456
  return;
412
457
  }
413
458
  const player = this.getPlayer(update.guild_id);
414
459
  if (!player) {
460
+ if (this.options?.advancedOptions?.enableDebugEvents) {
461
+ this.emit("debug", DebugEvents.NoAudioDebug, {
462
+ state: "warn",
463
+ message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${JSON.stringify(update, null, 2)}`,
464
+ functionLayer: "LavalinkManager > sendRawData()",
465
+ });
466
+ }
415
467
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
416
468
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
417
469
  return;
418
470
  }
419
471
  if (player.get("internal_destroystatus") === true) {
472
+ if (this.options?.advancedOptions?.enableDebugEvents) {
473
+ this.emit("debug", DebugEvents.NoAudioDebug, {
474
+ state: "warn",
475
+ message: `Player is in a destroying state. can't signal the voice states`,
476
+ functionLayer: "LavalinkManager > sendRawData()",
477
+ });
478
+ }
420
479
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
421
480
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states");
422
481
  return;
@@ -434,12 +493,24 @@ export class LavalinkManager extends EventEmitter {
434
493
  }
435
494
  }
436
495
  });
496
+ if (this.options?.advancedOptions?.enableDebugEvents) {
497
+ this.emit("debug", DebugEvents.NoAudioDebug, {
498
+ state: "log",
499
+ message: `Sent updatePlayer for voice token session :: ${JSON.stringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: player.voice?.sessionId, }, update }, null, 2)}`,
500
+ functionLayer: "LavalinkManager > sendRawData()",
501
+ });
502
+ }
437
503
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
438
504
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: player.voice?.sessionId, } });
439
505
  return;
440
506
  }
441
507
  /* voice state update */
442
508
  if (update.user_id !== this.options?.client.id) {
509
+ this.emit("debug", DebugEvents.NoAudioDebug, {
510
+ state: "warn",
511
+ message: `voice update user is not equal to provided client id of the LavalinkManager.options.client.id :: user: "${update.user_id}" manager client id: "${this.options?.client.id}"`,
512
+ functionLayer: "LavalinkManager > sendRawData()",
513
+ });
443
514
  if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
444
515
  console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, voice update user is not equal to provided client id of the manageroptions#client#id", "user:", update.user_id, "manager client id:", this.options?.client.id);
445
516
  return;
@@ -458,7 +529,11 @@ export class LavalinkManager extends EventEmitter {
458
529
  if (this.options?.playerOptions?.onDisconnect?.autoReconnect === true) {
459
530
  try {
460
531
  const positionPrevios = player.position;
461
- console.debug("Auto reconnect", positionPrevios, player.lastPosition);
532
+ this.emit("debug", DebugEvents.PlayerAutoReconnect, {
533
+ state: "log",
534
+ message: `Auto reconnecting player because LavalinkManager.options.playerOptions.onDisconnect.autoReconnect is true`,
535
+ functionLayer: "LavalinkManager > sendRawData()",
536
+ });
462
537
  await player.connect();
463
538
  // replay the current playing stream
464
539
  await player.play({