poru 1.2.3 → 2.0.2
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/.gitpod.yml +8 -0
- package/README.md +41 -28
- package/package.json +13 -2
- package/src/Node.js +126 -106
- package/src/Player.js +39 -20
- package/src/Poru.js +37 -12
- package/src/config.js +20 -0
- package/src/guild/Filter.js +3 -4
- package/src/guild/PoruTrack.js +61 -0
- package/src/guild/Track.js +55 -13
- package/src/platform/AppleMusic.js +248 -0
- package/src/platform/Deezer.js +233 -0
- package/src/platform/Spotify.js +246 -252
- package/typings/index.d.ts +28 -50
- package/src/config.json +0 -3
package/.gitpod.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# This configuration file was automatically generated by Gitpod.
|
|
2
|
+
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
|
3
|
+
# and commit this file to your remote git repository to share the goodness with others.
|
|
4
|
+
|
|
5
|
+
tasks:
|
|
6
|
+
- init: npm install
|
|
7
|
+
|
|
8
|
+
|
package/README.md
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
<p align="center">
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
[](https://discord.gg/Zmmc47Nrh8)
|
|
7
7
|
[](https://www.npmjs.com/package/poru)
|
|
8
8
|

|
|
9
9
|

|
|
10
|
-

|
|
10
|
+

|
|
11
11
|

|
|
12
12
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
<p align="center">
|
|
17
16
|
<a href="https://nodei.co/npm/poru/"><img src="https://nodei.co/npm/poru.png?downloads=true&downloadRank=true&stars=true"></a>
|
|
18
17
|
</p>
|
|
@@ -25,6 +24,7 @@
|
|
|
25
24
|
- [Example](https://github.com/parasop/poru-example)
|
|
26
25
|
|
|
27
26
|
# Installation
|
|
27
|
+
|
|
28
28
|
```
|
|
29
29
|
// Using npm
|
|
30
30
|
npm install poru
|
|
@@ -34,6 +34,7 @@ yarn add poru
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
# About
|
|
37
|
+
|
|
37
38
|
To use you need a configured [Lavalink](https://github.com/Frederikam/Lavalink) instance.
|
|
38
39
|
|
|
39
40
|
- Stable client
|
|
@@ -43,15 +44,16 @@ To use you need a configured [Lavalink](https://github.com/Frederikam/Lavalink)
|
|
|
43
44
|
- Easy to setup
|
|
44
45
|
|
|
45
46
|
## Implementation
|
|
46
|
-
[Poru Music](https://github.com/parasop/poru-example) **Example bot as guide for beginning.**
|
|
47
47
|
|
|
48
|
+
[Poru Music](https://github.com/parasop/poru-example) **Example bot as guide for beginning.**
|
|
48
49
|
|
|
49
50
|
## Example usage basic bot
|
|
51
|
+
|
|
50
52
|
```javascript
|
|
51
53
|
// main file
|
|
52
54
|
// Require both libraries
|
|
53
|
-
const { Client } = require(
|
|
54
|
-
const { Poru } = require(
|
|
55
|
+
const { Client } = require('discord.js');
|
|
56
|
+
const { Poru } = require('poru');
|
|
55
57
|
|
|
56
58
|
// Initiate both main classes
|
|
57
59
|
const client = new Client();
|
|
@@ -59,39 +61,49 @@ const client = new Client();
|
|
|
59
61
|
// Define some options for the node
|
|
60
62
|
const nodes = [
|
|
61
63
|
{
|
|
62
|
-
host:
|
|
63
|
-
password:
|
|
64
|
+
host: 'localhost',
|
|
65
|
+
password: 'youshallnotpass',
|
|
64
66
|
port: 2333,
|
|
65
|
-
secure:false
|
|
66
|
-
}
|
|
67
|
+
secure: false,
|
|
68
|
+
},
|
|
67
69
|
];
|
|
68
70
|
|
|
71
|
+
// Define if you want to integrate spotify
|
|
72
|
+
const spotifyOptions = {
|
|
73
|
+
clientID: 'Your Client ID', // You'll find this on https://developers.spotify.com/dashboard/
|
|
74
|
+
clientSecret: 'Your Client Secret', // You'll find this on https://developers.spotify.com/dashboard/
|
|
75
|
+
playlistLimit: 10, // The amount of pages to load when a playlist is searched with each page having 50 tracks.
|
|
76
|
+
albumLimit: 5, // The amount of pages to load when a album is searched with each page having 50 tracks.
|
|
77
|
+
artistLimit: 5, // The amount of pages to load when a artist is searched with each page having 50 tracks.
|
|
78
|
+
searchMarket: 'IN', // The market from where the query should be searched from. Mainly this should contain your country.
|
|
79
|
+
};
|
|
80
|
+
|
|
69
81
|
// Assign Manager to the client variable
|
|
70
|
-
client.poru = new Poru(client,nodes);
|
|
82
|
+
client.poru = new Poru(client, nodes);
|
|
71
83
|
|
|
72
84
|
// Emitted whenever a node connects
|
|
73
|
-
client.poru.on(
|
|
74
|
-
|
|
75
|
-
})
|
|
85
|
+
client.poru.on('nodeConnect', node => {
|
|
86
|
+
console.log(`Node "${node.name}" connected.`);
|
|
87
|
+
});
|
|
76
88
|
|
|
77
89
|
// Emitted whenever a node encountered an error
|
|
78
|
-
client.poru.on(
|
|
79
|
-
|
|
80
|
-
})
|
|
90
|
+
client.poru.on('nodeError', (node, error) => {
|
|
91
|
+
console.log(`Node "${node.name}" encountered an error`);
|
|
92
|
+
});
|
|
81
93
|
|
|
82
94
|
// Listen for when the client becomes ready
|
|
83
|
-
client.once(
|
|
84
|
-
client.poru.init(client;
|
|
95
|
+
client.once('ready', () => {
|
|
96
|
+
client.poru.init(client);
|
|
85
97
|
console.log(`Logged in as ${client.user.tag}`);
|
|
86
98
|
});
|
|
87
99
|
|
|
88
|
-
//
|
|
89
|
-
client.
|
|
90
|
-
|
|
100
|
+
// this event used to make connections upto date with lavalink
|
|
101
|
+
client.on('raw', async d => await client.poru.packetUpdate(d));
|
|
91
102
|
|
|
103
|
+
// Finally login at the END of your code
|
|
104
|
+
client.login('your bot token here');
|
|
92
105
|
```
|
|
93
106
|
|
|
94
|
-
|
|
95
107
|
```javascript
|
|
96
108
|
// creating player
|
|
97
109
|
const player = await client.poru.createConnection({
|
|
@@ -99,12 +111,13 @@ const player = await client.poru.createConnection({
|
|
|
99
111
|
voiceChannel: message.member.voice.channel.id,
|
|
100
112
|
textChannel: message.channel,
|
|
101
113
|
selfDeaf: true,
|
|
102
|
-
selfMute: false,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const resolve = await client.poru.resolve('Ignite',
|
|
114
|
+
selfMute: false,
|
|
115
|
+
});
|
|
116
|
+
// Getting tracks
|
|
117
|
+
const resolve = await client.poru.resolve('Ignite', 'yt');
|
|
106
118
|
```
|
|
107
119
|
|
|
108
120
|
## Need Help?
|
|
109
|
-
|
|
121
|
+
|
|
122
|
+
Feel free to join our [discord server](https://discord.gg/Zmmc47Nrh8), Give us suggestions and advice about errors and new features.
|
|
110
123
|
with ❤️ by [Paras](https://github.com/parasop) .
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poru",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A stable and powefull lavalink client with so many features",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,5 +19,16 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"node-fetch": "^3.2.6",
|
|
21
21
|
"ws": "^8.8.0"
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"music",
|
|
25
|
+
"lavalink",
|
|
26
|
+
"poru",
|
|
27
|
+
"bot",
|
|
28
|
+
"spotify",
|
|
29
|
+
"apple-music",
|
|
30
|
+
"soundcloud",
|
|
31
|
+
"discordjs",
|
|
32
|
+
"eris"
|
|
33
|
+
]
|
|
23
34
|
}
|
package/src/Node.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
const WebSocket = require("ws");
|
|
2
|
-
const config = require("./config
|
|
2
|
+
const config = require("./config")
|
|
3
3
|
|
|
4
4
|
class Node {
|
|
5
5
|
constructor(manager,options,node) {
|
|
6
|
-
|
|
7
6
|
this.manager = manager
|
|
8
7
|
this.name= options.name || null;
|
|
9
8
|
this.host = options.host || "localhost"
|
|
10
9
|
this.port = options.port || 2333
|
|
11
|
-
this.url = `${options.secure ? 'wss' : 'ws'}://${options.host}:${options.port}`;
|
|
12
10
|
this.password = options.password ||"youshallnotpass"
|
|
13
11
|
this.secure = options.secure || false;
|
|
12
|
+
this.url = `${this.secure ? 'wss' : 'ws'}://${this.host}:${this.port}/`;
|
|
14
13
|
this.ws = null;
|
|
15
14
|
this.reconnectTime = node.reconnectTime || 5000;
|
|
16
15
|
this.resumeKey = node.resumeKey || null;
|
|
@@ -37,132 +36,153 @@ class Node {
|
|
|
37
36
|
};
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
#open(){
|
|
60
|
-
if (this.reconnectAttempt) {
|
|
61
|
-
clearTimeout(this.reconnectAttempt);
|
|
62
|
-
delete this.reconnectAttempt;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (this.resumeKey){
|
|
66
|
-
this.send({
|
|
67
|
-
op: "configureResuming",
|
|
68
|
-
key: (this.resumeKey).toString(),
|
|
69
|
-
timeout: this.resumeTimeout
|
|
70
|
-
});
|
|
71
|
-
this.manager.emit("debug",this.name,`[Web Socket] Resuming configured on Lavalink`)
|
|
72
|
-
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
connect() {
|
|
43
|
+
if (this.ws) this.ws.close();
|
|
44
|
+
const headers = {
|
|
45
|
+
Authorization: this.password,
|
|
46
|
+
"Num-Shards": this.manager.shards || 1,
|
|
47
|
+
"User-Id": this.manager.user,
|
|
48
|
+
"Client-Name": config.clientName
|
|
49
|
+
};
|
|
50
|
+
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey;
|
|
51
|
+
this.ws = new WebSocket(this.url, { headers });
|
|
52
|
+
this.ws.on("open",this.#open.bind(this));
|
|
53
|
+
this.ws.on("error", this.#error.bind(this));
|
|
54
|
+
this.ws.on("message", this.#message.bind(this));
|
|
55
|
+
this.ws.on("close", this.#close.bind(this));
|
|
56
|
+
}
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
this.isConnected
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
58
|
+
disconnect(){
|
|
59
|
+
if(!this.isConnected) return;
|
|
60
|
+
|
|
61
|
+
this.ws?.removeAllListeners();
|
|
62
|
+
this.ws?.close();
|
|
63
|
+
this.ws = null;
|
|
64
|
+
this.isConnected = false;
|
|
79
65
|
|
|
80
|
-
#message(payload) {
|
|
81
|
-
if (Array.isArray(payload)) payload = Buffer.concat(payload);
|
|
82
|
-
else if (payload instanceof ArrayBuffer) payload = Buffer.from(payload);
|
|
83
66
|
|
|
84
|
-
const packet = JSON.parse(payload);
|
|
85
|
-
if (packet.op && packet.op === "stats") {
|
|
86
|
-
this.stats = { ...packet };
|
|
87
|
-
delete this.stats.op;
|
|
88
67
|
}
|
|
89
|
-
const player = this.manager.players.get(packet.guildId);
|
|
90
|
-
if (packet.guildId && player) player.emit(packet.op, packet);
|
|
91
68
|
|
|
92
|
-
packet.node = this;
|
|
93
|
-
this.manager.emit("debug",this.name,`[Web Socket] Lavalink Node Update : ${packet.op} `)
|
|
94
|
-
this.manager.emit("raw", packet);
|
|
95
|
-
}
|
|
96
69
|
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
70
|
+
#open(){
|
|
71
|
+
if (this.reconnectAttempt) {
|
|
72
|
+
clearTimeout(this.reconnectAttempt);
|
|
73
|
+
delete this.reconnectAttempt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.resumeKey){
|
|
77
|
+
this.send({
|
|
78
|
+
op: "configureResuming",
|
|
79
|
+
key: (this.resumeKey).toString(),
|
|
80
|
+
timeout: this.resumeTimeout
|
|
81
|
+
});
|
|
82
|
+
this.manager.emit("debug",this.name,`[Web Socket] Resuming configured on Lavalink`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.manager.emit("nodeConnect", this);
|
|
86
|
+
this.isConnected = true;
|
|
87
|
+
this.manager.emit('debug', this.name, `[Web Socket] Connection ready ${this.url}`);
|
|
88
|
+
|
|
89
|
+
if(config.autoResume){
|
|
90
|
+
|
|
91
|
+
for (const player of this.manager.players.values()) {
|
|
92
|
+
if (player.node === this) {
|
|
93
|
+
player.restart();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
}
|
|
101
103
|
|
|
102
|
-
return this.reconnect();
|
|
103
104
|
}
|
|
104
|
-
}
|
|
105
105
|
|
|
106
|
+
#message(payload) {
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const packet = JSON.parse(payload);
|
|
109
|
+
if(!packet.op) return;
|
|
110
|
+
|
|
111
|
+
if (packet.op && packet.op === "stats") {
|
|
112
|
+
this.stats = { ...packet };
|
|
113
|
+
}
|
|
114
|
+
const player = this.manager.players.get(packet.guildId);
|
|
115
|
+
if (packet.guildId && player) player.emit(packet.op, packet);
|
|
116
|
+
packet.node = this;
|
|
117
|
+
this.manager.emit("debug",this.name,`[Web Socket] Lavalink Node Update : ${packet.op} `)
|
|
118
|
+
this.manager.emit("raw", packet);
|
|
119
|
+
}
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
121
|
+
#close(event) {
|
|
122
|
+
this.disconnect();
|
|
123
|
+
this.manager.emit("nodeDisconnect",this,event);
|
|
124
|
+
this.manager.emit("debug",this.name,`[Web Socket] Connection with Lavalink closed with Error code : ${event||"Unknown code"}`)
|
|
125
|
+
if (event !== 1000){
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
}
|
|
114
129
|
|
|
115
|
-
destroy(){
|
|
116
|
-
if(!this.isConnected) return;
|
|
117
130
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.ws.close(1000, "destroy");
|
|
121
|
-
this.ws.removeAllListeners();
|
|
122
|
-
this.ws = null;
|
|
123
|
-
this.reconnect = 1;
|
|
124
|
-
this.destroyed = true;
|
|
125
|
-
this.manager.nodes.delete(this.host)
|
|
126
|
-
this.manager.emit("nodeDestroy", this);
|
|
131
|
+
#error(event) {
|
|
132
|
+
if (!event) return "Unknown event";
|
|
127
133
|
|
|
134
|
+
this.manager.emit("debug",this.name,`[Web Socket] Connection for Lavalink node has error code: ${event.code}`)
|
|
135
|
+
this.manager.emit("nodeError", this, event);
|
|
136
|
+
return this.reconnect();
|
|
137
|
+
}
|
|
128
138
|
|
|
129
|
-
|
|
139
|
+
destroy(){
|
|
140
|
+
if(!this.isConnected) return;
|
|
130
141
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
142
|
+
const players = this.manager.players.filter(p => p.node == this);
|
|
143
|
+
if (players.size) players.forEach(p => p.destroy());
|
|
144
|
+
this.ws.close(1000, "destroy");
|
|
134
145
|
this.ws.removeAllListeners();
|
|
135
146
|
this.ws = null;
|
|
136
|
-
this.
|
|
137
|
-
this.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
send(payload) {
|
|
142
|
-
this.ws.send(JSON.stringify(payload), (error) => {
|
|
143
|
-
if (error) return error;
|
|
144
|
-
return null;
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
+
this.reconnect = 1;
|
|
148
|
+
this.destroyed = true;
|
|
149
|
+
this.manager.nodes.delete(this.host)
|
|
150
|
+
this.manager.emit("nodeDestroy", this);
|
|
147
151
|
|
|
148
152
|
|
|
149
|
-
|
|
150
|
-
get penalties(){
|
|
151
|
-
let penalties = 0;
|
|
152
|
-
if (!this.isConnected) return penalties;
|
|
153
|
-
penalties += this.stats.players;
|
|
154
|
-
penalties += Math.round(Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10);
|
|
155
|
-
if (this.stats.frameStats) {
|
|
156
|
-
penalties += this.stats.frameStats.deficit;
|
|
157
|
-
penalties += this.stats.frameStats.nulled * 2;
|
|
158
153
|
}
|
|
159
|
-
return penalties;
|
|
160
|
-
}
|
|
161
154
|
|
|
155
|
+
reconnect() {
|
|
156
|
+
this.reconnectAttempt = setTimeout(() => {
|
|
157
|
+
this.isConnected = false;
|
|
158
|
+
this.ws.removeAllListeners();
|
|
159
|
+
this.ws = null;
|
|
160
|
+
this.manager.emit("nodeReconnect", this);
|
|
161
|
+
this.connect();
|
|
162
|
+
}, this.reconnectTime);
|
|
163
|
+
}
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
send(payload) {
|
|
166
|
+
const data = JSON.stringify(payload);
|
|
167
|
+
this.ws.send(data, (error) => {
|
|
168
|
+
if (error) return error;
|
|
169
|
+
return null;
|
|
170
|
+
});
|
|
171
|
+
this.manager.emit("raw", data, this.name)
|
|
172
|
+
}
|
|
165
173
|
|
|
174
|
+
get penalties(){
|
|
175
|
+
let penalties = 0;
|
|
176
|
+
if (!this.isConnected) return penalties;
|
|
177
|
+
penalties += this.stats.players;
|
|
178
|
+
penalties += Math.round(Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10);
|
|
179
|
+
if (this.stats.frameStats) {
|
|
180
|
+
penalties += this.stats.frameStats.deficit;
|
|
181
|
+
penalties += this.stats.frameStats.nulled * 2;
|
|
182
|
+
}
|
|
183
|
+
return penalties;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
166
186
|
|
|
167
|
-
|
|
187
|
+
module.exports = Node;
|
|
168
188
|
|
package/src/Player.js
CHANGED
|
@@ -23,7 +23,7 @@ class Player extends EventEmitter {
|
|
|
23
23
|
|
|
24
24
|
this.isPlaying = false;
|
|
25
25
|
|
|
26
|
-
this.
|
|
26
|
+
this.isPaused = false;
|
|
27
27
|
|
|
28
28
|
this.trackRepeat = false;
|
|
29
29
|
|
|
@@ -50,26 +50,32 @@ class Player extends EventEmitter {
|
|
|
50
50
|
this.manager.emit("playerUpdate", this, packet);
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
async play(options={}) {
|
|
54
55
|
|
|
55
56
|
if (!this.queue.length) {
|
|
56
|
-
return
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.currentTrack = this.queue.shift()
|
|
61
|
+
|
|
62
|
+
if(!this.currentTrack.track){
|
|
63
|
+
this.currentTrack = await this.currentTrack.resolve(this.manager);
|
|
57
64
|
}
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
this.timestamp = Date.now();
|
|
65
|
+
|
|
66
|
+
this.isPlaying = true;
|
|
61
67
|
this.node.send({
|
|
62
68
|
op: "play",
|
|
63
69
|
guildId: this.guild,
|
|
64
70
|
track: this.currentTrack.track,
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
noReplace:options.noReplace || true,
|
|
72
|
+
});
|
|
67
73
|
this.position = 0;
|
|
68
74
|
return this;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
stop() {
|
|
73
79
|
|
|
74
80
|
this.position = 0;
|
|
75
81
|
this.isConnectd = false
|
|
@@ -90,7 +96,7 @@ class Player extends EventEmitter {
|
|
|
90
96
|
pause,
|
|
91
97
|
});
|
|
92
98
|
this.isPlaying = !pause;
|
|
93
|
-
this.
|
|
99
|
+
this.isPaused = pause;
|
|
94
100
|
|
|
95
101
|
return this;
|
|
96
102
|
}
|
|
@@ -125,6 +131,7 @@ class Player extends EventEmitter {
|
|
|
125
131
|
return this;
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
|
|
128
135
|
async QueueRepeat() {
|
|
129
136
|
this.loop = 2;
|
|
130
137
|
this.queueRepeat = true;
|
|
@@ -136,9 +143,7 @@ class Player extends EventEmitter {
|
|
|
136
143
|
this.loop = 0;
|
|
137
144
|
this.trackRepeat = false;
|
|
138
145
|
this.queueRepeat = false;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return this;
|
|
146
|
+
return this;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
async setTextChannel(channel) {
|
|
@@ -179,9 +184,6 @@ class Player extends EventEmitter {
|
|
|
179
184
|
return this;
|
|
180
185
|
}
|
|
181
186
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
187
|
async disconnect() {
|
|
186
188
|
if (this.voiceChannel === null) return null;
|
|
187
189
|
this.pause(true);
|
|
@@ -209,16 +211,34 @@ class Player extends EventEmitter {
|
|
|
209
211
|
this.manager.players.delete(this.guild);
|
|
210
212
|
}
|
|
211
213
|
|
|
214
|
+
restart(){
|
|
215
|
+
this.filters.updateFilters();
|
|
216
|
+
if(this.currentTrack){
|
|
217
|
+
|
|
218
|
+
this.isPlaying = true;
|
|
219
|
+
this.node.send({
|
|
220
|
+
op: "play",
|
|
221
|
+
startTime: this.position,
|
|
222
|
+
noReplace:true,
|
|
223
|
+
guildId: this.guild,
|
|
224
|
+
track: this.currentTrack.track,
|
|
225
|
+
puase: this.isPaused
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
212
232
|
async autoplay(toggle = false) {
|
|
213
233
|
|
|
214
234
|
if (!toggle) return null;
|
|
215
235
|
try {
|
|
216
236
|
if (!this.previousTrack) return this.stop();
|
|
217
|
-
let data = `https://www.youtube.com/watch?v=${this.previousTrack.identifier}&list=RD${this.previousTrack.identifier}`;
|
|
237
|
+
let data = `https://www.youtube.com/watch?v=${this.previousTrack.info.identifier}&list=RD${this.previousTrack.info.identifier}`;
|
|
218
238
|
|
|
219
|
-
let response = await this.manager.
|
|
239
|
+
let response = await this.manager.resolve(data);
|
|
220
240
|
|
|
221
|
-
if (!response || !response.tracks || ["LOAD_FAILED", "NO_MATCHES"].includes(response.
|
|
241
|
+
if (!response || !response.tracks || ["LOAD_FAILED", "NO_MATCHES"].includes(response.loadType)) return this.stop();
|
|
222
242
|
|
|
223
243
|
let track = response.tracks[Math.floor(Math.random() * Math.floor(response.tracks.length))];
|
|
224
244
|
|
|
@@ -303,7 +323,6 @@ class Player extends EventEmitter {
|
|
|
303
323
|
return events[data.type] || events.default;
|
|
304
324
|
}
|
|
305
325
|
|
|
306
|
-
|
|
307
326
|
}
|
|
308
327
|
|
|
309
328
|
module.exports = Player;
|
package/src/Poru.js
CHANGED
|
@@ -2,11 +2,13 @@ const { EventEmitter } = require("events");
|
|
|
2
2
|
const fetch = (...args) => import('node-fetch').then(({
|
|
3
3
|
default: fetch
|
|
4
4
|
}) => fetch(...args));
|
|
5
|
+
const config = require("./config")
|
|
5
6
|
const Player = require("./Player");
|
|
6
7
|
const Node = require("./Node");
|
|
7
8
|
const Response = require("./guild/Response");
|
|
8
|
-
const config = require("./config.json")
|
|
9
9
|
const Spotify = require("./platform/Spotify")
|
|
10
|
+
const AppleMusic = require("./platform/AppleMusic")
|
|
11
|
+
const Deezer = require("./platform/Deezer")
|
|
10
12
|
class Poru extends EventEmitter {
|
|
11
13
|
constructor(client, nodes, options = {}) {
|
|
12
14
|
super();
|
|
@@ -23,6 +25,7 @@ class Poru extends EventEmitter {
|
|
|
23
25
|
this.options = options
|
|
24
26
|
this.shards = options.shards || 1;
|
|
25
27
|
this.sendData = null;
|
|
28
|
+
this.version = config.version
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
|
|
@@ -75,21 +78,34 @@ class Poru extends EventEmitter {
|
|
|
75
78
|
if (guild) guild.shard.send(data);
|
|
76
79
|
}
|
|
77
80
|
client.on("raw", async packet => {
|
|
78
|
-
await this
|
|
81
|
+
await this.packetUpdate(packet);
|
|
79
82
|
})
|
|
80
|
-
|
|
83
|
+
|
|
81
84
|
this._nodes.forEach((node) => this.addNode(node));
|
|
82
85
|
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
this.spotify = new Spotify(this,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if (this.options.spotify && this.options.spotify.clientID && this.options.spotify.clientSecret) {
|
|
88
|
+
this.spotify = new Spotify(this, this.options)
|
|
89
|
+
}
|
|
90
|
+
if (this.options.apple) {
|
|
91
|
+
if (!this.options.apple.playlistLimit) {
|
|
92
|
+
throw new Error("[Poru Apple Music] playlistLimit must be provided")
|
|
93
|
+
}
|
|
94
|
+
this.apple = new AppleMusic(this, this.options)
|
|
95
|
+
this.apple.requestToken();
|
|
96
|
+
}
|
|
97
|
+
if (this.options.deezer) {
|
|
98
|
+
if (!this.options.deezer.playlistLimit) {
|
|
99
|
+
throw new Error("[Poru Deezer Music] playlistLimit must be provided")
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
this.deezer = new Deezer(this, this.options)
|
|
103
|
+
|
|
89
104
|
}
|
|
90
105
|
console.log(`Thanks for using Poru`)
|
|
91
106
|
}
|
|
92
107
|
|
|
108
|
+
|
|
93
109
|
setServersUpdate(data) {
|
|
94
110
|
let guild = data.guild_id
|
|
95
111
|
this.voiceServers.set(guild, data);
|
|
@@ -130,7 +146,7 @@ class Poru extends EventEmitter {
|
|
|
130
146
|
this.voiceStates.delete(data.guild_id);
|
|
131
147
|
}
|
|
132
148
|
|
|
133
|
-
|
|
149
|
+
packetUpdate(packet) {
|
|
134
150
|
if (!['VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE'].includes(packet.t)) return;
|
|
135
151
|
const player = this.players.get(packet.d.guild_id);
|
|
136
152
|
if (!player) return;
|
|
@@ -175,15 +191,24 @@ class Poru extends EventEmitter {
|
|
|
175
191
|
|
|
176
192
|
|
|
177
193
|
async resolve(track, source) {
|
|
194
|
+
|
|
178
195
|
const node = this.leastUsedNodes[0];
|
|
179
196
|
if (!node) throw new Error("No nodes are available.");
|
|
180
|
-
|
|
181
|
-
|
|
197
|
+
|
|
198
|
+
if (this.spotify && this.spotify.check(track)) {
|
|
199
|
+
return await this.spotify.resolve(track);
|
|
200
|
+
} else if (this.apple && this.apple.check(track)) {
|
|
201
|
+
return await this.apple.resolve(track);
|
|
202
|
+
} else if (this.deezer && this.deezer.check(track)) {
|
|
203
|
+
return await this.deezer.resolve(track);
|
|
182
204
|
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
183
208
|
const regex = /^https?:\/\//;
|
|
184
209
|
if (!regex.test(track)) {
|
|
185
210
|
// eslint-disable-next-line no-param-reassign
|
|
186
|
-
track = `${source || "
|
|
211
|
+
track = `${source || "ytsearch"}:${track}`;
|
|
187
212
|
}
|
|
188
213
|
const result = await this.#fetch(node, "loadtracks", `identifier=${encodeURIComponent(track)}`);
|
|
189
214
|
|