muthera 1.0.2 → 1.0.5

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/package.json CHANGED
@@ -1,31 +1,31 @@
1
- {
2
- "name": "muthera",
3
- "version": "1.0.2",
4
- "description": "A simple Lavalink wrapper for Discord music bot. Forked from Niizuki.",
5
- "main": "src/index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "urfavteddybear",
10
- "license": "ISC",
11
- "dependencies": {
12
- "@discordjs/collection": "^2.1.1",
13
- "jsdom": "^26.1.0",
14
- "ws": "^8.18.3"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/urfavteddybear/muthera.git"
19
- },
20
- "bugs": {
21
- "url": "https://github.com/urfavteddybear/muthera/issues"
22
- },
23
- "homepage": "https://github.com/urfavteddybear/muthera#readme",
24
- "keywords": [
25
- "wrapper",
26
- "discord",
27
- "lavalink-v4",
28
- "lavalink",
29
- "music-bot"
30
- ]
31
- }
1
+ {
2
+ "name": "muthera",
3
+ "version": "1.0.5",
4
+ "description": "A simple Lavalink wrapper for Discord music bot. Forked from Niizuki.",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "urfavteddybear",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "@discordjs/collection": "^2.1.1",
13
+ "jsdom": "^26.1.0",
14
+ "ws": "^8.18.3"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/urfavteddybear/muthera.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/urfavteddybear/muthera/issues"
22
+ },
23
+ "homepage": "https://github.com/urfavteddybear/muthera#readme",
24
+ "keywords": [
25
+ "wrapper",
26
+ "discord",
27
+ "lavalink-v4",
28
+ "lavalink",
29
+ "music-bot"
30
+ ]
31
+ }
package/src/index.d.ts CHANGED
@@ -381,7 +381,11 @@ export type Voice = {
381
381
  /**
382
382
  * The voice endpoint
383
383
  */
384
- endpoint: String
384
+ endpoint: String,
385
+ /**
386
+ * The voice channel id
387
+ */
388
+ channelId: String
385
389
  }
386
390
 
387
391
  export declare class Connection {
@@ -1,191 +1,191 @@
1
- const { EventEmitter } = require("events");
2
- const { Node } = require("./mutheraNode");
3
- const { Player } = require("./mutheraPlayer");
4
- const { Track } = require("./mutheraTrack");
5
- const { Collection } = require("@discordjs/collection");
6
-
7
- class Muthera extends EventEmitter {
8
- constructor(client, nodes, options) {
9
- super();
10
- if (!client)
11
- throw new Error("Client option must be present to initialize muthera.");
12
- if (!nodes)
13
- throw new Error("Node option must be present to initialize muthera.");
14
- if (!options.send)
15
- throw new Error("Send function must be present to initialize muthera.");
16
-
17
- this.client = client;
18
- this.nodes = nodes;
19
- this.nodeMap = new Collection();
20
- this.players = new Collection();
21
- this.options = options;
22
- this.clientId = null;
23
- this.initiated = false;
24
- this.send = options.send || null;
25
- this.defaultSearchPlatform = options.defaultSearchPlatform || "ytsearch";
26
- this.tracks = [];
27
- this.loadType = null;
28
- this.playlistInfo = null;
29
- }
30
-
31
- get leastUsedNodes() {
32
- return [...this.nodeMap.values()]
33
- .filter((node) => node.connected)
34
- .sort((a, b) => b.rest.calls - a.rest.calls);
35
- }
36
-
37
- init(clientId) {
38
- if (this.initiated) return this;
39
- this.clientId = clientId;
40
- this.nodes.forEach((node) => this.createNode(node));
41
- this.initiated = true;
42
- }
43
-
44
- createNode(options) {
45
- const node = new Node(this, options, this.options);
46
- this.nodeMap.set(options.name || options.host, node);
47
- node.connect();
48
-
49
- this.emit("nodeCreate", node);
50
- return node;
51
- }
52
-
53
- destroyNode(identifier) {
54
- const node = this.nodeMap.get(identifier);
55
- if (!node) return;
56
- node.disconnect();
57
- this.nodeMap.delete(identifier);
58
- this.emit("nodeDestroy", node);
59
- }
60
-
61
- updateVoiceState(packet) {
62
- if (!["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(packet.t))
63
- return;
64
- const player = this.players.get(packet.d.guild_id);
65
- if (!player) return;
66
-
67
- if (packet.t === "VOICE_SERVER_UPDATE") {
68
- player.connection.setServerUpdate(packet.d);
69
- } else if (packet.t === "VOICE_STATE_UPDATE") {
70
- if (packet.d.user_id !== this.clientId) return;
71
- player.connection.setStateUpdate(packet.d);
72
- }
73
- }
74
-
75
- fetchRegion(region) {
76
- const nodesByRegion = [...this.nodeMap.values()]
77
- .filter(
78
- (node) =>
79
- node.connected && node.regions?.includes(region?.toLowerCase())
80
- )
81
- .sort((a, b) => {
82
- const aLoad = a.stats.cpu
83
- ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100
84
- : 0;
85
- const bLoad = b.stats.cpu
86
- ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100
87
- : 0;
88
- return aLoad - bLoad;
89
- });
90
-
91
- return nodesByRegion;
92
- }
93
-
94
- createConnection(options) {
95
- if (!this.initiated)
96
- throw new Error("You have to initialize muthera in your event.");
97
-
98
- const player = this.players.get(options.guildId);
99
- if (player) return player;
100
-
101
- if (this.leastUsedNodes.length === 0)
102
- throw new Error("No nodes are available.");
103
-
104
- let node;
105
- if (options.node) {
106
- node = this.nodeMap.get(options.node);
107
- } else {
108
- node = this.nodeMap.get(this.leastUsedNodes[0].name);
109
- }
110
-
111
- if (!node) throw new Error("No nodes are available.");
112
-
113
- return this.createPlayer(node, options);
114
- }
115
-
116
- createPlayer(node, options) {
117
- const player = new Player(this, node, options);
118
- this.players.set(options.guildId, player);
119
-
120
- player.connect(options);
121
-
122
- this.emit("playerCreate", player);
123
- return player;
124
- }
125
-
126
- destroyPlayer(guildId) {
127
- const player = this.players.get(guildId);
128
- if (!player) return;
129
- player.destroy();
130
- this.players.delete(guildId);
131
-
132
- this.emit("playerDestroy", player);
133
- }
134
-
135
- removeConnection(guildId) {
136
- this.players.get(guildId)?.destroy();
137
- this.players.delete(guildId);
138
- }
139
-
140
- async resolve({ query, source, requester }) {
141
- try {
142
- if (!this.initiated)
143
- throw new Error("You have to initialize muthera in your event.");
144
-
145
- const sources = source || this.defaultSearchPlatform;
146
-
147
- const node = this.leastUsedNodes[0];
148
- if (!node) throw new Error("No nodes are available.");
149
-
150
- const regex = /^https?:\/\//;
151
- const identifier = regex.test(query) ? query : `${sources}:${query}`;
152
-
153
- let response = await node.rest.makeRequest(
154
- `GET`,
155
- `/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`
156
- );
157
-
158
- if (response.loadType === "track") {
159
- this.tracks = [new Track(response.data, requester, node)];
160
- } else if (response.loadType === "playlist") {
161
- this.tracks = response.data.tracks.map(
162
- (track) => new Track(track, requester, node)
163
- );
164
- } else if (
165
- response.loadType === "error" ||
166
- response.loadType === "empty"
167
- ) {
168
- this.tracks = null;
169
- } else {
170
- this.tracks = response.data.map(
171
- (track) => new Track(track, requester, node)
172
- );
173
- }
174
-
175
- this.playlistInfo = response.data.info ?? null;
176
- this.loadType = response.loadType ?? null;
177
- return this;
178
- } catch (error) {
179
- throw new Error(error);
180
- }
181
- }
182
-
183
- get(guildId) {
184
- const player = this.players.get(guildId);
185
- if (!player)
186
- throw new Error(`No player were found for guildId: ${guildId}.`);
187
- return player;
188
- }
189
- }
190
-
191
- module.exports = { Muthera };
1
+ const { EventEmitter } = require("events");
2
+ const { Node } = require("./mutheraNode");
3
+ const { Player } = require("./mutheraPlayer");
4
+ const { Track } = require("./mutheraTrack");
5
+ const { Collection } = require("@discordjs/collection");
6
+
7
+ class Muthera extends EventEmitter {
8
+ constructor(client, nodes, options) {
9
+ super();
10
+ if (!client)
11
+ throw new Error("Client option must be present to initialize muthera.");
12
+ if (!nodes)
13
+ throw new Error("Node option must be present to initialize muthera.");
14
+ if (!options.send)
15
+ throw new Error("Send function must be present to initialize muthera.");
16
+
17
+ this.client = client;
18
+ this.nodes = nodes;
19
+ this.nodeMap = new Collection();
20
+ this.players = new Collection();
21
+ this.options = options;
22
+ this.clientId = null;
23
+ this.initiated = false;
24
+ this.send = options.send || null;
25
+ this.defaultSearchPlatform = options.defaultSearchPlatform || "ytsearch";
26
+ this.tracks = [];
27
+ this.loadType = null;
28
+ this.playlistInfo = null;
29
+ }
30
+
31
+ get leastUsedNodes() {
32
+ return [...this.nodeMap.values()]
33
+ .filter((node) => node.connected)
34
+ .sort((a, b) => b.rest.calls - a.rest.calls);
35
+ }
36
+
37
+ init(clientId) {
38
+ if (this.initiated) return this;
39
+ this.clientId = clientId;
40
+ this.nodes.forEach((node) => this.createNode(node));
41
+ this.initiated = true;
42
+ }
43
+
44
+ createNode(options) {
45
+ const node = new Node(this, options, this.options);
46
+ this.nodeMap.set(options.name || options.host, node);
47
+ node.connect();
48
+
49
+ this.emit("nodeCreate", node);
50
+ return node;
51
+ }
52
+
53
+ destroyNode(identifier) {
54
+ const node = this.nodeMap.get(identifier);
55
+ if (!node) return;
56
+ node.disconnect();
57
+ this.nodeMap.delete(identifier);
58
+ this.emit("nodeDestroy", node);
59
+ }
60
+
61
+ updateVoiceState(packet) {
62
+ if (!["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(packet.t))
63
+ return;
64
+ const player = this.players.get(packet.d.guild_id);
65
+ if (!player) return;
66
+
67
+ if (packet.t === "VOICE_SERVER_UPDATE") {
68
+ player.connection.setServerUpdate(packet.d);
69
+ } else if (packet.t === "VOICE_STATE_UPDATE") {
70
+ if (packet.d.user_id !== this.clientId) return;
71
+ player.connection.setStateUpdate(packet.d);
72
+ }
73
+ }
74
+
75
+ fetchRegion(region) {
76
+ const nodesByRegion = [...this.nodeMap.values()]
77
+ .filter(
78
+ (node) =>
79
+ node.connected && node.regions?.includes(region?.toLowerCase())
80
+ )
81
+ .sort((a, b) => {
82
+ const aLoad = a.stats.cpu
83
+ ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100
84
+ : 0;
85
+ const bLoad = b.stats.cpu
86
+ ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100
87
+ : 0;
88
+ return aLoad - bLoad;
89
+ });
90
+
91
+ return nodesByRegion;
92
+ }
93
+
94
+ createConnection(options) {
95
+ if (!this.initiated)
96
+ throw new Error("You have to initialize muthera in your event.");
97
+
98
+ const player = this.players.get(options.guildId);
99
+ if (player) return player;
100
+
101
+ if (this.leastUsedNodes.length === 0)
102
+ throw new Error("No nodes are available.");
103
+
104
+ let node;
105
+ if (options.node) {
106
+ node = this.nodeMap.get(options.node);
107
+ } else {
108
+ node = this.nodeMap.get(this.leastUsedNodes[0].name);
109
+ }
110
+
111
+ if (!node) throw new Error("No nodes are available.");
112
+
113
+ return this.createPlayer(node, options);
114
+ }
115
+
116
+ createPlayer(node, options) {
117
+ const player = new Player(this, node, options);
118
+ this.players.set(options.guildId, player);
119
+
120
+ player.connect(options);
121
+
122
+ this.emit("playerCreate", player);
123
+ return player;
124
+ }
125
+
126
+ destroyPlayer(guildId) {
127
+ const player = this.players.get(guildId);
128
+ if (!player) return;
129
+ player.destroy();
130
+ this.players.delete(guildId);
131
+
132
+ this.emit("playerDestroy", player);
133
+ }
134
+
135
+ removeConnection(guildId) {
136
+ this.players.get(guildId)?.destroy();
137
+ this.players.delete(guildId);
138
+ }
139
+
140
+ async resolve({ query, source, requester }) {
141
+ try {
142
+ if (!this.initiated)
143
+ throw new Error("You have to initialize muthera in your event.");
144
+
145
+ const sources = source || this.defaultSearchPlatform;
146
+
147
+ const node = this.leastUsedNodes[0];
148
+ if (!node) throw new Error("No nodes are available.");
149
+
150
+ const regex = /^https?:\/\//;
151
+ const identifier = regex.test(query) ? query : `${sources}:${query}`;
152
+
153
+ let response = await node.rest.makeRequest(
154
+ `GET`,
155
+ `/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`
156
+ );
157
+
158
+ if (response.loadType === "track") {
159
+ this.tracks = [new Track(response.data, requester, node)];
160
+ } else if (response.loadType === "playlist") {
161
+ this.tracks = response.data.tracks.map(
162
+ (track) => new Track(track, requester, node)
163
+ );
164
+ } else if (
165
+ response.loadType === "error" ||
166
+ response.loadType === "empty"
167
+ ) {
168
+ this.tracks = null;
169
+ } else {
170
+ this.tracks = response.data.map(
171
+ (track) => new Track(track, requester, node)
172
+ );
173
+ }
174
+
175
+ this.playlistInfo = response.data.info ?? null;
176
+ this.loadType = response.loadType ?? null;
177
+ return this;
178
+ } catch (error) {
179
+ throw new Error(error);
180
+ }
181
+ }
182
+
183
+ get(guildId) {
184
+ const player = this.players.get(guildId);
185
+ if (!player)
186
+ throw new Error(`No player were found for guildId: ${guildId}.`);
187
+ return player;
188
+ }
189
+ }
190
+
191
+ module.exports = { Muthera };
@@ -9,6 +9,7 @@ class Connection {
9
9
  event: null,
10
10
  endpoint: null,
11
11
  sessionId: null,
12
+ channelId: null,
12
13
  };
13
14
  this.self_deaf = false;
14
15
  this.self_mute = false;
@@ -48,6 +49,7 @@ class Connection {
48
49
  this.self_deaf = self_deaf;
49
50
  this.self_mute = self_mute;
50
51
  this.voice.sessionId = session_id || null;
52
+ this.voice.channelId = channel_id || null;
51
53
  }
52
54
 
53
55
  updatePlayerVoiceData() {
@@ -66,10 +66,11 @@ class Node {
66
66
  }
67
67
 
68
68
  open() {
69
- if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
69
+ if (this.reconnectAttempt) clearTimeout(this.reconnectAttempt);
70
70
 
71
71
  this.muthera.emit("nodeConnect", this);
72
72
  this.connected = true;
73
+ this.reconnectAttempted = 1; // Reset reconnection attempts on successful connection
73
74
  this.muthera.emit(
74
75
  "debug",
75
76
  this.name,
@@ -138,7 +139,7 @@ class Node {
138
139
 
139
140
  reconnect() {
140
141
  this.reconnectAttempt = setTimeout(() => {
141
- if (this.reconnectAttempted >= this.reconnectTries) {
142
+ if (this.reconnectAttempted > this.reconnectTries) {
142
143
  const error = new Error(
143
144
  `Unable to connect node: ${this.name} after ${this.reconnectTries} attempts.`
144
145
  );
@@ -147,9 +148,10 @@ class Node {
147
148
  return this.destroy();
148
149
  }
149
150
 
150
- this.ws.removeAllListeners();
151
+ this.ws?.removeAllListeners();
151
152
  this.ws = null;
152
153
  this.muthera.emit("nodeReconnect", this);
154
+ this.muthera.emit("debug", this.name, `Reconnection attempt ${this.reconnectAttempted}/${this.reconnectTries}`);
153
155
  this.connect();
154
156
  this.reconnectAttempted++;
155
157
  }, this.reconnectTimeout);