lavalink-client 1.1.7 → 1.1.9
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.
- package/dist/cjs/structures/LavalinkManager.d.ts +7 -2
- package/dist/cjs/structures/LavalinkManager.js +82 -68
- package/dist/cjs/structures/LavalinkManagerStatics.d.ts +7 -0
- package/dist/cjs/structures/LavalinkManagerStatics.js +8 -1
- package/dist/cjs/structures/Utils.js +6 -7
- package/dist/esm/structures/LavalinkManager.d.ts +7 -2
- package/dist/esm/structures/LavalinkManager.js +82 -68
- package/dist/esm/structures/LavalinkManagerStatics.d.ts +7 -0
- package/dist/esm/structures/LavalinkManagerStatics.js +7 -0
- package/dist/esm/structures/Utils.js +7 -8
- package/dist/types/structures/LavalinkManager.d.ts +7 -2
- package/dist/types/structures/LavalinkManagerStatics.d.ts +7 -0
- package/package.json +1 -1
|
@@ -56,6 +56,11 @@ export interface ManagerOptions {
|
|
|
56
56
|
playerOptions?: ManagerPlayerOptions;
|
|
57
57
|
/** If it should skip to the next Track on TrackEnd / TrackError etc. events */
|
|
58
58
|
autoSkip?: boolean;
|
|
59
|
+
/** optional */
|
|
60
|
+
debugOptions?: {
|
|
61
|
+
/** logs for debugging the "no-Audio" playing error */
|
|
62
|
+
noAudio: boolean;
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
interface LavalinkManagerEvents {
|
|
61
66
|
/**
|
|
@@ -124,8 +129,8 @@ export declare class LavalinkManager extends EventEmitter {
|
|
|
124
129
|
static SourceLinksRegexes: Record<import("./Utils").SourcesRegex, RegExp>;
|
|
125
130
|
initiated: boolean;
|
|
126
131
|
readonly players: MiniMap<string, Player>;
|
|
127
|
-
private
|
|
128
|
-
private
|
|
132
|
+
private applyOptions;
|
|
133
|
+
private validateOptions;
|
|
129
134
|
constructor(options: ManagerOptions);
|
|
130
135
|
createPlayer(options: PlayerOptions): Player;
|
|
131
136
|
getPlayer(guildId: string): Player;
|
|
@@ -12,94 +12,83 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
12
12
|
static SourceLinksRegexes = LavalinkManagerStatics_1.SourceLinksRegexes;
|
|
13
13
|
initiated = false;
|
|
14
14
|
players = new Utils_1.MiniMap();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
applyOptions(options) {
|
|
16
|
+
this.options = {
|
|
17
|
+
client: {
|
|
18
|
+
...(options?.client || {}),
|
|
19
|
+
id: options?.client?.id,
|
|
20
|
+
username: options?.client?.username ?? "lavalink-client"
|
|
21
|
+
},
|
|
22
|
+
sendToShard: options?.sendToShard,
|
|
23
|
+
nodes: options?.nodes,
|
|
24
|
+
playerOptions: {
|
|
25
|
+
applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
|
|
26
|
+
clientBasedPositionUpdateInterval: options?.playerOptions?.clientBasedPositionUpdateInterval ?? 100,
|
|
27
|
+
defaultSearchPlatform: options?.playerOptions?.defaultSearchPlatform ?? "ytsearch",
|
|
21
28
|
onDisconnect: {
|
|
22
|
-
destroyPlayer: true,
|
|
23
|
-
autoReconnect: false
|
|
29
|
+
destroyPlayer: options?.playerOptions?.onDisconnect?.destroyPlayer ?? true,
|
|
30
|
+
autoReconnect: options?.playerOptions?.onDisconnect?.autoReconnect ?? false
|
|
24
31
|
},
|
|
25
32
|
onEmptyQueue: {
|
|
26
|
-
autoPlayFunction: null,
|
|
27
|
-
destroyAfterMs: undefined
|
|
33
|
+
autoPlayFunction: options?.playerOptions?.onEmptyQueue?.autoPlayFunction ?? null,
|
|
34
|
+
destroyAfterMs: options?.playerOptions?.onEmptyQueue?.destroyAfterMs ?? undefined
|
|
28
35
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
volumeDecrementer: 1
|
|
45
|
-
};
|
|
46
|
-
if (!this.options.autoSkip)
|
|
47
|
-
this.options.autoSkip = true;
|
|
48
|
-
if (!this.options.playerOptions.defaultSearchPlatform)
|
|
49
|
-
this.options.playerOptions.defaultSearchPlatform = "ytsearch";
|
|
50
|
-
// default queue options
|
|
51
|
-
if (!this.options.queueOptions)
|
|
52
|
-
this.options.queueOptions = {
|
|
53
|
-
maxPreviousTracks: 25,
|
|
54
|
-
queueChangesWatcher: null,
|
|
55
|
-
queueStore: new Queue_1.DefaultQueueStore()
|
|
56
|
-
};
|
|
57
|
-
if (typeof this.options?.queueOptions?.maxPreviousTracks !== "number" || this.options.queueOptions.maxPreviousTracks < 0)
|
|
58
|
-
this.options.queueOptions.maxPreviousTracks = 25;
|
|
36
|
+
volumeDecrementer: options?.playerOptions?.volumeDecrementer ?? 1,
|
|
37
|
+
requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
|
|
38
|
+
useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
|
|
39
|
+
},
|
|
40
|
+
autoSkip: options?.autoSkip ?? true,
|
|
41
|
+
queueOptions: {
|
|
42
|
+
maxPreviousTracks: options?.queueOptions?.maxPreviousTracks ?? 25,
|
|
43
|
+
queueChangesWatcher: options?.queueOptions?.queueChangesWatcher ?? null,
|
|
44
|
+
queueStore: options?.queueOptions?.queueStore ?? new Queue_1.DefaultQueueStore(),
|
|
45
|
+
},
|
|
46
|
+
debugOptions: {
|
|
47
|
+
noAudio: options?.debugOptions?.noAudio ?? false
|
|
48
|
+
}
|
|
49
|
+
};
|
|
59
50
|
return;
|
|
60
51
|
}
|
|
61
|
-
|
|
62
|
-
if (typeof options
|
|
52
|
+
validateOptions(options) {
|
|
53
|
+
if (typeof options?.sendToShard !== "function")
|
|
63
54
|
throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
|
|
64
55
|
// only check in .init()
|
|
65
|
-
// if(typeof options
|
|
66
|
-
if (options
|
|
56
|
+
// if(typeof options?.client !== "object" || typeof options?.client.id !== "string") throw new SyntaxError("ManagerOption.client = { id: string, username?:string } was not provided, which is required");
|
|
57
|
+
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
67
58
|
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
68
|
-
if (!options
|
|
59
|
+
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every(node => this.utils.isNodeOptions(node)))
|
|
69
60
|
throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
|
|
70
61
|
/* QUEUE STORE */
|
|
71
|
-
if (options
|
|
72
|
-
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options
|
|
62
|
+
if (options?.queueOptions?.queueStore) {
|
|
63
|
+
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueStore));
|
|
73
64
|
const requiredKeys = ["get", "set", "stringify", "parse", "delete"];
|
|
74
|
-
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options
|
|
65
|
+
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
75
66
|
throw new SyntaxError(`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`);
|
|
76
67
|
}
|
|
77
|
-
else
|
|
78
|
-
this.options.queueOptions.queueStore = new Queue_1.DefaultQueueStore();
|
|
79
68
|
/* QUEUE WATCHER */
|
|
80
|
-
if (options
|
|
81
|
-
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options
|
|
69
|
+
if (options?.queueOptions?.queueChangesWatcher) {
|
|
70
|
+
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueChangesWatcher));
|
|
82
71
|
const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
|
|
83
|
-
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options
|
|
72
|
+
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
84
73
|
throw new SyntaxError(`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`);
|
|
85
74
|
}
|
|
75
|
+
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
76
|
+
options.queueOptions.maxPreviousTracks = 25;
|
|
86
77
|
}
|
|
87
78
|
constructor(options) {
|
|
88
79
|
super();
|
|
89
80
|
if (!options)
|
|
90
81
|
throw new SyntaxError("No Manager Options Provided");
|
|
91
|
-
// create options
|
|
92
|
-
this.options = options;
|
|
93
82
|
this.utils = new Utils_1.ManagerUtils(this);
|
|
94
83
|
// use the validators
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
84
|
+
this.applyOptions(options);
|
|
85
|
+
this.validateOptions(options);
|
|
97
86
|
// create classes
|
|
98
87
|
this.nodeManager = new NodeManager_1.NodeManager(this);
|
|
99
88
|
}
|
|
100
89
|
createPlayer(options) {
|
|
101
|
-
if (this.players.has(options
|
|
102
|
-
return this.players.get(options
|
|
90
|
+
if (this.players.has(options?.guildId))
|
|
91
|
+
return this.players.get(options?.guildId);
|
|
103
92
|
const newPlayer = new Player_1.Player(options, this);
|
|
104
93
|
this.players.set(newPlayer.guildId, newPlayer);
|
|
105
94
|
return newPlayer;
|
|
@@ -122,10 +111,11 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
122
111
|
async init(clientData) {
|
|
123
112
|
if (this.initiated)
|
|
124
113
|
return this;
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
clientData = clientData ?? {};
|
|
115
|
+
this.options.client = { ...(this.options?.client || {}), ...clientData };
|
|
116
|
+
if (!this.options?.client.id)
|
|
127
117
|
throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
|
|
128
|
-
if (typeof this.options
|
|
118
|
+
if (typeof this.options?.client.id !== "string")
|
|
129
119
|
throw new Error('"client.id" set is not type of "string"');
|
|
130
120
|
let success = 0;
|
|
131
121
|
for (const node of [...this.nodeManager.nodes.values()]) {
|
|
@@ -149,8 +139,16 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
149
139
|
* @param data
|
|
150
140
|
*/
|
|
151
141
|
async sendRawData(data) {
|
|
152
|
-
if (!this.initiated
|
|
142
|
+
if (!this.initiated) {
|
|
143
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
144
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!("t" in data)) {
|
|
148
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
149
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
|
|
153
150
|
return;
|
|
151
|
+
}
|
|
154
152
|
// for channel Delete
|
|
155
153
|
if ("CHANNEL_DELETE" === data.t) {
|
|
156
154
|
const update = "d" in data ? data.d : data;
|
|
@@ -163,11 +161,22 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
163
161
|
// for voice updates
|
|
164
162
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
165
163
|
const update = "d" in data ? data.d : data;
|
|
166
|
-
if (!update
|
|
164
|
+
if (!update) {
|
|
165
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
166
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!("token" in update) && !("session_id" in update)) {
|
|
170
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
171
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
|
|
167
172
|
return;
|
|
173
|
+
}
|
|
168
174
|
const player = this.getPlayer(update.guild_id);
|
|
169
|
-
if (!player)
|
|
175
|
+
if (!player) {
|
|
176
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
177
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
|
|
170
178
|
return;
|
|
179
|
+
}
|
|
171
180
|
if ("token" in update) {
|
|
172
181
|
if (!player.node?.sessionId)
|
|
173
182
|
throw new Error("Lavalink Node is either not ready or not up to date");
|
|
@@ -181,11 +190,16 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
});
|
|
193
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
194
|
+
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, } });
|
|
184
195
|
return;
|
|
185
196
|
}
|
|
186
197
|
/* voice state update */
|
|
187
|
-
if (update.user_id !== this.options
|
|
198
|
+
if (update.user_id !== this.options?.client.id) {
|
|
199
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
200
|
+
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);
|
|
188
201
|
return;
|
|
202
|
+
}
|
|
189
203
|
if (update.channel_id) {
|
|
190
204
|
if (player.voiceChannelId !== update.channel_id)
|
|
191
205
|
this.emit("playerMove", player, player.voiceChannelId, update.channel_id);
|
|
@@ -193,12 +207,12 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
193
207
|
player.voiceChannelId = update.channel_id;
|
|
194
208
|
}
|
|
195
209
|
else {
|
|
196
|
-
if (this.options
|
|
210
|
+
if (this.options?.playerOptions?.onDisconnect?.destroyPlayer === true) {
|
|
197
211
|
return await player.destroy(Player_1.DestroyReasons.Disconnected);
|
|
198
212
|
}
|
|
199
213
|
this.emit("playerDisconnect", player, player.voiceChannelId);
|
|
200
214
|
await player.pause();
|
|
201
|
-
if (this.options
|
|
215
|
+
if (this.options?.playerOptions?.onDisconnect?.autoReconnect === true) {
|
|
202
216
|
try {
|
|
203
217
|
await player.connect();
|
|
204
218
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { LavalinkSearchPlatform, SearchPlatform, SourcesRegex } from "./Utils";
|
|
2
2
|
export declare const DefaultSources: Record<SearchPlatform, LavalinkSearchPlatform>;
|
|
3
|
+
export declare const LavalinkPlugins: {
|
|
4
|
+
DuncteBot_Plugin: string;
|
|
5
|
+
LavaSrc: string;
|
|
6
|
+
GoogleCloudTTS: string;
|
|
7
|
+
LavaSearch: string;
|
|
8
|
+
LavalinkFilterPlugin: string;
|
|
9
|
+
};
|
|
3
10
|
export declare const SourceLinksRegexes: Record<SourcesRegex, RegExp>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SourceLinksRegexes = exports.DefaultSources = void 0;
|
|
3
|
+
exports.SourceLinksRegexes = exports.LavalinkPlugins = exports.DefaultSources = void 0;
|
|
4
4
|
exports.DefaultSources = {
|
|
5
5
|
// youtubemusic
|
|
6
6
|
"youtube music": "ytmsearch",
|
|
@@ -42,6 +42,13 @@ exports.DefaultSources = {
|
|
|
42
42
|
"tts": "tts",
|
|
43
43
|
"ftts": "ftts"
|
|
44
44
|
};
|
|
45
|
+
exports.LavalinkPlugins = {
|
|
46
|
+
DuncteBot_Plugin: "DuncteBot-plugin",
|
|
47
|
+
LavaSrc: "lavasrc-plugin",
|
|
48
|
+
GoogleCloudTTS: "tts-plugin",
|
|
49
|
+
LavaSearch: "lavasearch-plugin",
|
|
50
|
+
LavalinkFilterPlugin: "lavalink-filter-plugin"
|
|
51
|
+
};
|
|
45
52
|
exports.SourceLinksRegexes = {
|
|
46
53
|
/** DEFAULT SUPPORTED BY LAVALINK */
|
|
47
54
|
YoutubeRegex: /https?:\/\/?(?:www\.)?(?:(m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
|
|
@@ -218,15 +218,14 @@ class ManagerUtils {
|
|
|
218
218
|
if (source === "scsearch" && !node.info.sourceManagers.includes("soundcloud")) {
|
|
219
219
|
throw new Error("Lavalink Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
|
|
220
220
|
}
|
|
221
|
-
if (source === "speak" && !node.info.
|
|
221
|
+
if (source === "speak" && !node.info.plugins.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
|
|
222
222
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
223
223
|
}
|
|
224
|
-
if (source === "tts" && !node.info.
|
|
224
|
+
if (source === "tts" && !node.info.plugins.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
|
|
225
225
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
226
226
|
}
|
|
227
|
-
if (source === "ftts" && !node.info.sourceManagers.includes("ftts")
|
|
228
|
-
|
|
229
|
-
throw new Error("Lavalink Node has not 'ftts' enabled, which is required to have 'ftts' work");
|
|
227
|
+
if (source === "ftts" && !(node.info.sourceManagers.includes("ftts") || node.info.sourceManagers.includes("flowery-tts") || node.info.sourceManagers.includes("flowerytts"))) {
|
|
228
|
+
throw new Error("Lavalink Node has not 'flowery-tts' enabled, which is required to have 'ftts' work");
|
|
230
229
|
}
|
|
231
230
|
if (source === "ymsearch" && !node.info.sourceManagers.includes("yandexmusic")) {
|
|
232
231
|
throw new Error("Lavalink Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
|
|
@@ -293,9 +292,9 @@ exports.queueTrackEnd = queueTrackEnd;
|
|
|
293
292
|
async function applyUnresolvedData(resTrack, data, utils) {
|
|
294
293
|
if (!resTrack?.info || !data?.info)
|
|
295
294
|
return;
|
|
295
|
+
if (data.info.uri)
|
|
296
|
+
resTrack.info.uri = data.info.uri;
|
|
296
297
|
if (utils?.LavalinkManager?.options?.playerOptions?.useUnresolvedData === true) { // overwrite values
|
|
297
|
-
if (data.info.uri)
|
|
298
|
-
resTrack.info.uri = data.info.uri;
|
|
299
298
|
if (data.info.artworkUrl?.length)
|
|
300
299
|
resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
301
300
|
if (data.info.title?.length)
|
|
@@ -56,6 +56,11 @@ export interface ManagerOptions {
|
|
|
56
56
|
playerOptions?: ManagerPlayerOptions;
|
|
57
57
|
/** If it should skip to the next Track on TrackEnd / TrackError etc. events */
|
|
58
58
|
autoSkip?: boolean;
|
|
59
|
+
/** optional */
|
|
60
|
+
debugOptions?: {
|
|
61
|
+
/** logs for debugging the "no-Audio" playing error */
|
|
62
|
+
noAudio: boolean;
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
interface LavalinkManagerEvents {
|
|
61
66
|
/**
|
|
@@ -124,8 +129,8 @@ export declare class LavalinkManager extends EventEmitter {
|
|
|
124
129
|
static SourceLinksRegexes: Record<import("./Utils").SourcesRegex, RegExp>;
|
|
125
130
|
initiated: boolean;
|
|
126
131
|
readonly players: MiniMap<string, Player>;
|
|
127
|
-
private
|
|
128
|
-
private
|
|
132
|
+
private applyOptions;
|
|
133
|
+
private validateOptions;
|
|
129
134
|
constructor(options: ManagerOptions);
|
|
130
135
|
createPlayer(options: PlayerOptions): Player;
|
|
131
136
|
getPlayer(guildId: string): Player;
|
|
@@ -9,94 +9,83 @@ export class LavalinkManager extends EventEmitter {
|
|
|
9
9
|
static SourceLinksRegexes = SourceLinksRegexes;
|
|
10
10
|
initiated = false;
|
|
11
11
|
players = new MiniMap();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
applyOptions(options) {
|
|
13
|
+
this.options = {
|
|
14
|
+
client: {
|
|
15
|
+
...(options?.client || {}),
|
|
16
|
+
id: options?.client?.id,
|
|
17
|
+
username: options?.client?.username ?? "lavalink-client"
|
|
18
|
+
},
|
|
19
|
+
sendToShard: options?.sendToShard,
|
|
20
|
+
nodes: options?.nodes,
|
|
21
|
+
playerOptions: {
|
|
22
|
+
applyVolumeAsFilter: options?.playerOptions?.applyVolumeAsFilter ?? false,
|
|
23
|
+
clientBasedPositionUpdateInterval: options?.playerOptions?.clientBasedPositionUpdateInterval ?? 100,
|
|
24
|
+
defaultSearchPlatform: options?.playerOptions?.defaultSearchPlatform ?? "ytsearch",
|
|
18
25
|
onDisconnect: {
|
|
19
|
-
destroyPlayer: true,
|
|
20
|
-
autoReconnect: false
|
|
26
|
+
destroyPlayer: options?.playerOptions?.onDisconnect?.destroyPlayer ?? true,
|
|
27
|
+
autoReconnect: options?.playerOptions?.onDisconnect?.autoReconnect ?? false
|
|
21
28
|
},
|
|
22
29
|
onEmptyQueue: {
|
|
23
|
-
autoPlayFunction: null,
|
|
24
|
-
destroyAfterMs: undefined
|
|
30
|
+
autoPlayFunction: options?.playerOptions?.onEmptyQueue?.autoPlayFunction ?? null,
|
|
31
|
+
destroyAfterMs: options?.playerOptions?.onEmptyQueue?.destroyAfterMs ?? undefined
|
|
25
32
|
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
volumeDecrementer: 1
|
|
42
|
-
};
|
|
43
|
-
if (!this.options.autoSkip)
|
|
44
|
-
this.options.autoSkip = true;
|
|
45
|
-
if (!this.options.playerOptions.defaultSearchPlatform)
|
|
46
|
-
this.options.playerOptions.defaultSearchPlatform = "ytsearch";
|
|
47
|
-
// default queue options
|
|
48
|
-
if (!this.options.queueOptions)
|
|
49
|
-
this.options.queueOptions = {
|
|
50
|
-
maxPreviousTracks: 25,
|
|
51
|
-
queueChangesWatcher: null,
|
|
52
|
-
queueStore: new DefaultQueueStore()
|
|
53
|
-
};
|
|
54
|
-
if (typeof this.options?.queueOptions?.maxPreviousTracks !== "number" || this.options.queueOptions.maxPreviousTracks < 0)
|
|
55
|
-
this.options.queueOptions.maxPreviousTracks = 25;
|
|
33
|
+
volumeDecrementer: options?.playerOptions?.volumeDecrementer ?? 1,
|
|
34
|
+
requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
|
|
35
|
+
useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
|
|
36
|
+
},
|
|
37
|
+
autoSkip: options?.autoSkip ?? true,
|
|
38
|
+
queueOptions: {
|
|
39
|
+
maxPreviousTracks: options?.queueOptions?.maxPreviousTracks ?? 25,
|
|
40
|
+
queueChangesWatcher: options?.queueOptions?.queueChangesWatcher ?? null,
|
|
41
|
+
queueStore: options?.queueOptions?.queueStore ?? new DefaultQueueStore(),
|
|
42
|
+
},
|
|
43
|
+
debugOptions: {
|
|
44
|
+
noAudio: options?.debugOptions?.noAudio ?? false
|
|
45
|
+
}
|
|
46
|
+
};
|
|
56
47
|
return;
|
|
57
48
|
}
|
|
58
|
-
|
|
59
|
-
if (typeof options
|
|
49
|
+
validateOptions(options) {
|
|
50
|
+
if (typeof options?.sendToShard !== "function")
|
|
60
51
|
throw new SyntaxError("ManagerOption.sendToShard was not provided, which is required!");
|
|
61
52
|
// only check in .init()
|
|
62
|
-
// if(typeof options
|
|
63
|
-
if (options
|
|
53
|
+
// if(typeof options?.client !== "object" || typeof options?.client.id !== "string") throw new SyntaxError("ManagerOption.client = { id: string, username?:string } was not provided, which is required");
|
|
54
|
+
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
64
55
|
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
65
|
-
if (!options
|
|
56
|
+
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every(node => this.utils.isNodeOptions(node)))
|
|
66
57
|
throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
|
|
67
58
|
/* QUEUE STORE */
|
|
68
|
-
if (options
|
|
69
|
-
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options
|
|
59
|
+
if (options?.queueOptions?.queueStore) {
|
|
60
|
+
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueStore));
|
|
70
61
|
const requiredKeys = ["get", "set", "stringify", "parse", "delete"];
|
|
71
|
-
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options
|
|
62
|
+
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options?.queueOptions?.queueStore[v] === "function"))
|
|
72
63
|
throw new SyntaxError(`The provided ManagerOption.QueueStore, does not have all required functions: ${requiredKeys.join(", ")}`);
|
|
73
64
|
}
|
|
74
|
-
else
|
|
75
|
-
this.options.queueOptions.queueStore = new DefaultQueueStore();
|
|
76
65
|
/* QUEUE WATCHER */
|
|
77
|
-
if (options
|
|
78
|
-
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options
|
|
66
|
+
if (options?.queueOptions?.queueChangesWatcher) {
|
|
67
|
+
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options?.queueOptions?.queueChangesWatcher));
|
|
79
68
|
const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
|
|
80
|
-
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options
|
|
69
|
+
if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options?.queueOptions?.queueChangesWatcher[v] === "function"))
|
|
81
70
|
throw new SyntaxError(`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`);
|
|
82
71
|
}
|
|
72
|
+
if (typeof options?.queueOptions?.maxPreviousTracks !== "number" || options?.queueOptions?.maxPreviousTracks < 0)
|
|
73
|
+
options.queueOptions.maxPreviousTracks = 25;
|
|
83
74
|
}
|
|
84
75
|
constructor(options) {
|
|
85
76
|
super();
|
|
86
77
|
if (!options)
|
|
87
78
|
throw new SyntaxError("No Manager Options Provided");
|
|
88
|
-
// create options
|
|
89
|
-
this.options = options;
|
|
90
79
|
this.utils = new ManagerUtils(this);
|
|
91
80
|
// use the validators
|
|
92
|
-
this.
|
|
93
|
-
this.
|
|
81
|
+
this.applyOptions(options);
|
|
82
|
+
this.validateOptions(options);
|
|
94
83
|
// create classes
|
|
95
84
|
this.nodeManager = new NodeManager(this);
|
|
96
85
|
}
|
|
97
86
|
createPlayer(options) {
|
|
98
|
-
if (this.players.has(options
|
|
99
|
-
return this.players.get(options
|
|
87
|
+
if (this.players.has(options?.guildId))
|
|
88
|
+
return this.players.get(options?.guildId);
|
|
100
89
|
const newPlayer = new Player(options, this);
|
|
101
90
|
this.players.set(newPlayer.guildId, newPlayer);
|
|
102
91
|
return newPlayer;
|
|
@@ -119,10 +108,11 @@ export class LavalinkManager extends EventEmitter {
|
|
|
119
108
|
async init(clientData) {
|
|
120
109
|
if (this.initiated)
|
|
121
110
|
return this;
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
clientData = clientData ?? {};
|
|
112
|
+
this.options.client = { ...(this.options?.client || {}), ...clientData };
|
|
113
|
+
if (!this.options?.client.id)
|
|
124
114
|
throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
|
|
125
|
-
if (typeof this.options
|
|
115
|
+
if (typeof this.options?.client.id !== "string")
|
|
126
116
|
throw new Error('"client.id" set is not type of "string"');
|
|
127
117
|
let success = 0;
|
|
128
118
|
for (const node of [...this.nodeManager.nodes.values()]) {
|
|
@@ -146,8 +136,16 @@ export class LavalinkManager extends EventEmitter {
|
|
|
146
136
|
* @param data
|
|
147
137
|
*/
|
|
148
138
|
async sendRawData(data) {
|
|
149
|
-
if (!this.initiated
|
|
139
|
+
if (!this.initiated) {
|
|
140
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
141
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!("t" in data)) {
|
|
145
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
146
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
|
|
150
147
|
return;
|
|
148
|
+
}
|
|
151
149
|
// for channel Delete
|
|
152
150
|
if ("CHANNEL_DELETE" === data.t) {
|
|
153
151
|
const update = "d" in data ? data.d : data;
|
|
@@ -160,11 +158,22 @@ export class LavalinkManager extends EventEmitter {
|
|
|
160
158
|
// for voice updates
|
|
161
159
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
162
160
|
const update = "d" in data ? data.d : data;
|
|
163
|
-
if (!update
|
|
161
|
+
if (!update) {
|
|
162
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
163
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!("token" in update) && !("session_id" in update)) {
|
|
167
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
168
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
|
|
164
169
|
return;
|
|
170
|
+
}
|
|
165
171
|
const player = this.getPlayer(update.guild_id);
|
|
166
|
-
if (!player)
|
|
172
|
+
if (!player) {
|
|
173
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
174
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
|
|
167
175
|
return;
|
|
176
|
+
}
|
|
168
177
|
if ("token" in update) {
|
|
169
178
|
if (!player.node?.sessionId)
|
|
170
179
|
throw new Error("Lavalink Node is either not ready or not up to date");
|
|
@@ -178,11 +187,16 @@ export class LavalinkManager extends EventEmitter {
|
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
189
|
});
|
|
190
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
191
|
+
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, } });
|
|
181
192
|
return;
|
|
182
193
|
}
|
|
183
194
|
/* voice state update */
|
|
184
|
-
if (update.user_id !== this.options
|
|
195
|
+
if (update.user_id !== this.options?.client.id) {
|
|
196
|
+
if (this.options?.debugOptions?.noAudio === true)
|
|
197
|
+
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);
|
|
185
198
|
return;
|
|
199
|
+
}
|
|
186
200
|
if (update.channel_id) {
|
|
187
201
|
if (player.voiceChannelId !== update.channel_id)
|
|
188
202
|
this.emit("playerMove", player, player.voiceChannelId, update.channel_id);
|
|
@@ -190,12 +204,12 @@ export class LavalinkManager extends EventEmitter {
|
|
|
190
204
|
player.voiceChannelId = update.channel_id;
|
|
191
205
|
}
|
|
192
206
|
else {
|
|
193
|
-
if (this.options
|
|
207
|
+
if (this.options?.playerOptions?.onDisconnect?.destroyPlayer === true) {
|
|
194
208
|
return await player.destroy(DestroyReasons.Disconnected);
|
|
195
209
|
}
|
|
196
210
|
this.emit("playerDisconnect", player, player.voiceChannelId);
|
|
197
211
|
await player.pause();
|
|
198
|
-
if (this.options
|
|
212
|
+
if (this.options?.playerOptions?.onDisconnect?.autoReconnect === true) {
|
|
199
213
|
try {
|
|
200
214
|
await player.connect();
|
|
201
215
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { LavalinkSearchPlatform, SearchPlatform, SourcesRegex } from "./Utils";
|
|
2
2
|
export declare const DefaultSources: Record<SearchPlatform, LavalinkSearchPlatform>;
|
|
3
|
+
export declare const LavalinkPlugins: {
|
|
4
|
+
DuncteBot_Plugin: string;
|
|
5
|
+
LavaSrc: string;
|
|
6
|
+
GoogleCloudTTS: string;
|
|
7
|
+
LavaSearch: string;
|
|
8
|
+
LavalinkFilterPlugin: string;
|
|
9
|
+
};
|
|
3
10
|
export declare const SourceLinksRegexes: Record<SourcesRegex, RegExp>;
|
|
@@ -39,6 +39,13 @@ export const DefaultSources = {
|
|
|
39
39
|
"tts": "tts",
|
|
40
40
|
"ftts": "ftts"
|
|
41
41
|
};
|
|
42
|
+
export const LavalinkPlugins = {
|
|
43
|
+
DuncteBot_Plugin: "DuncteBot-plugin",
|
|
44
|
+
LavaSrc: "lavasrc-plugin",
|
|
45
|
+
GoogleCloudTTS: "tts-plugin",
|
|
46
|
+
LavaSearch: "lavasearch-plugin",
|
|
47
|
+
LavalinkFilterPlugin: "lavalink-filter-plugin"
|
|
48
|
+
};
|
|
42
49
|
export const SourceLinksRegexes = {
|
|
43
50
|
/** DEFAULT SUPPORTED BY LAVALINK */
|
|
44
51
|
YoutubeRegex: /https?:\/\/?(?:www\.)?(?:(m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultSources, SourceLinksRegexes } from "./LavalinkManagerStatics";
|
|
1
|
+
import { DefaultSources, LavalinkPlugins, SourceLinksRegexes } from "./LavalinkManagerStatics";
|
|
2
2
|
export const TrackSymbol = Symbol("LC-Track");
|
|
3
3
|
export const UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
4
4
|
export const QueueSymbol = Symbol("LC-Queue");
|
|
@@ -215,15 +215,14 @@ export class ManagerUtils {
|
|
|
215
215
|
if (source === "scsearch" && !node.info.sourceManagers.includes("soundcloud")) {
|
|
216
216
|
throw new Error("Lavalink Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
|
|
217
217
|
}
|
|
218
|
-
if (source === "speak" && !node.info.
|
|
218
|
+
if (source === "speak" && !node.info.plugins.find(c => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
|
|
219
219
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
220
220
|
}
|
|
221
|
-
if (source === "tts" && !node.info.
|
|
221
|
+
if (source === "tts" && !node.info.plugins.find(c => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
|
|
222
222
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
223
223
|
}
|
|
224
|
-
if (source === "ftts" && !node.info.sourceManagers.includes("ftts")
|
|
225
|
-
|
|
226
|
-
throw new Error("Lavalink Node has not 'ftts' enabled, which is required to have 'ftts' work");
|
|
224
|
+
if (source === "ftts" && !(node.info.sourceManagers.includes("ftts") || node.info.sourceManagers.includes("flowery-tts") || node.info.sourceManagers.includes("flowerytts"))) {
|
|
225
|
+
throw new Error("Lavalink Node has not 'flowery-tts' enabled, which is required to have 'ftts' work");
|
|
227
226
|
}
|
|
228
227
|
if (source === "ymsearch" && !node.info.sourceManagers.includes("yandexmusic")) {
|
|
229
228
|
throw new Error("Lavalink Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
|
|
@@ -287,9 +286,9 @@ export async function queueTrackEnd(player) {
|
|
|
287
286
|
async function applyUnresolvedData(resTrack, data, utils) {
|
|
288
287
|
if (!resTrack?.info || !data?.info)
|
|
289
288
|
return;
|
|
289
|
+
if (data.info.uri)
|
|
290
|
+
resTrack.info.uri = data.info.uri;
|
|
290
291
|
if (utils?.LavalinkManager?.options?.playerOptions?.useUnresolvedData === true) { // overwrite values
|
|
291
|
-
if (data.info.uri)
|
|
292
|
-
resTrack.info.uri = data.info.uri;
|
|
293
292
|
if (data.info.artworkUrl?.length)
|
|
294
293
|
resTrack.info.artworkUrl = data.info.artworkUrl;
|
|
295
294
|
if (data.info.title?.length)
|
|
@@ -56,6 +56,11 @@ export interface ManagerOptions {
|
|
|
56
56
|
playerOptions?: ManagerPlayerOptions;
|
|
57
57
|
/** If it should skip to the next Track on TrackEnd / TrackError etc. events */
|
|
58
58
|
autoSkip?: boolean;
|
|
59
|
+
/** optional */
|
|
60
|
+
debugOptions?: {
|
|
61
|
+
/** logs for debugging the "no-Audio" playing error */
|
|
62
|
+
noAudio: boolean;
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
interface LavalinkManagerEvents {
|
|
61
66
|
/**
|
|
@@ -124,8 +129,8 @@ export declare class LavalinkManager extends EventEmitter {
|
|
|
124
129
|
static SourceLinksRegexes: Record<import("./Utils").SourcesRegex, RegExp>;
|
|
125
130
|
initiated: boolean;
|
|
126
131
|
readonly players: MiniMap<string, Player>;
|
|
127
|
-
private
|
|
128
|
-
private
|
|
132
|
+
private applyOptions;
|
|
133
|
+
private validateOptions;
|
|
129
134
|
constructor(options: ManagerOptions);
|
|
130
135
|
createPlayer(options: PlayerOptions): Player;
|
|
131
136
|
getPlayer(guildId: string): Player;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { LavalinkSearchPlatform, SearchPlatform, SourcesRegex } from "./Utils";
|
|
2
2
|
export declare const DefaultSources: Record<SearchPlatform, LavalinkSearchPlatform>;
|
|
3
|
+
export declare const LavalinkPlugins: {
|
|
4
|
+
DuncteBot_Plugin: string;
|
|
5
|
+
LavaSrc: string;
|
|
6
|
+
GoogleCloudTTS: string;
|
|
7
|
+
LavaSearch: string;
|
|
8
|
+
LavalinkFilterPlugin: string;
|
|
9
|
+
};
|
|
3
10
|
export declare const SourceLinksRegexes: Record<SourcesRegex, RegExp>;
|
package/package.json
CHANGED