muthera 1.0.4 → 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 +31 -31
- package/src/index.d.ts +5 -1
- package/src/structures/Muthera.js +191 -191
- package/src/structures/mutheraConnection.js +2 -0
- package/src/structures/mutheraPlayer.js +524 -524
- package/src/structures/mutheraTrack.js +96 -96
package/package.json
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "muthera",
|
|
3
|
-
"version": "1.0.
|
|
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
|
@@ -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() {
|