poru 1.1.8 → 1.2.1
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 +39 -27
- package/package.json +16 -7
- package/src/Node.js +105 -108
- package/src/Player.js +3 -6
- package/src/Poru.js +33 -29
- package/src/config.js +3 -0
- package/src/guild/DeezerTrack.js +61 -0
- package/src/platform/Apple.js +5 -4
- package/src/platform/Deezer.js +200 -0
- package/src/platform/Spotify.js +244 -249
- package/typings/index.d.ts +27 -48
- 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,40 +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(
|
|
95
|
+
client.once('ready', () => {
|
|
84
96
|
client.poru.init(client);
|
|
85
97
|
console.log(`Logged in as ${client.user.tag}`);
|
|
86
98
|
});
|
|
87
99
|
|
|
100
|
+
// this event used to make connections upto date with lavalink
|
|
101
|
+
client.on('raw', async d => await client.poru.packetUpdate(d));
|
|
88
102
|
|
|
89
103
|
// Finally login at the END of your code
|
|
90
|
-
client.login(
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
client.login('your bot token here');
|
|
93
105
|
```
|
|
94
106
|
|
|
95
|
-
|
|
96
107
|
```javascript
|
|
97
108
|
// creating player
|
|
98
109
|
const player = await client.poru.createConnection({
|
|
@@ -100,12 +111,13 @@ const player = await client.poru.createConnection({
|
|
|
100
111
|
voiceChannel: message.member.voice.channel.id,
|
|
101
112
|
textChannel: message.channel,
|
|
102
113
|
selfDeaf: true,
|
|
103
|
-
selfMute: false,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const resolve = await client.poru.resolve('Ignite',
|
|
114
|
+
selfMute: false,
|
|
115
|
+
});
|
|
116
|
+
// Getting tracks
|
|
117
|
+
const resolve = await client.poru.resolve('Ignite', 'yt');
|
|
107
118
|
```
|
|
108
119
|
|
|
109
120
|
## Need Help?
|
|
110
|
-
|
|
121
|
+
|
|
122
|
+
Feel free to join our [discord server](https://discord.gg/Zmmc47Nrh8), Give us suggestions and advice about errors and new features.
|
|
111
123
|
with ❤️ by [Paras](https://github.com/parasop) .
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poru",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A stable and powefull lavalink client with so many features",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,10 +17,19 @@
|
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/parasop/poru#readme",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
"cheerio": "^1.0.0-rc.12",
|
|
21
|
+
"undici": "^5.6.0",
|
|
22
|
+
"ws": "^8.8.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"music",
|
|
26
|
+
"lavalink",
|
|
27
|
+
"poru",
|
|
28
|
+
"bot",
|
|
29
|
+
"spotify",
|
|
30
|
+
"apple-music",
|
|
31
|
+
"soundcloud",
|
|
32
|
+
"discordjs",
|
|
33
|
+
"eris"
|
|
34
|
+
]
|
|
26
35
|
}
|
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,130 @@ class Node {
|
|
|
37
36
|
};
|
|
38
37
|
|
|
39
38
|
|
|
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.client
|
|
49
|
-
};
|
|
50
|
-
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey;
|
|
51
|
-
this.ws = new WebSocket(`ws${this.secure ? "s" : ""}:${this.host}:${this.port}/`, { 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
|
-
}
|
|
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
|
-
}
|
|
73
|
-
|
|
74
|
-
this.manager.emit("nodeConnect", this);
|
|
75
|
-
this.isConnected = true;
|
|
76
|
-
this.manager.emit('debug', this.name, `[Web Socket] Connection ready ${this.url}`);
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
#message(payload) {
|
|
81
|
-
if (Array.isArray(payload)) payload = Buffer.concat(payload);
|
|
82
|
-
else if (payload instanceof ArrayBuffer) payload = Buffer.from(payload);
|
|
39
|
+
}
|
|
83
40
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
41
|
+
connect() {
|
|
42
|
+
if (this.ws) this.ws.close();
|
|
43
|
+
const headers = {
|
|
44
|
+
Authorization: this.password,
|
|
45
|
+
"Num-Shards": this.manager.shards || 1,
|
|
46
|
+
"User-Id": this.manager.user,
|
|
47
|
+
"Client-Name": config.clientName
|
|
48
|
+
};
|
|
49
|
+
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey;
|
|
50
|
+
this.ws = new WebSocket(this.url, { headers });
|
|
51
|
+
this.ws.on("open",this.#open.bind(this));
|
|
52
|
+
this.ws.on("error", this.#error.bind(this));
|
|
53
|
+
this.ws.on("message", this.#message.bind(this));
|
|
54
|
+
this.ws.on("close", this.#close.bind(this));
|
|
88
55
|
}
|
|
89
|
-
const player = this.manager.players.get(packet.guildId);
|
|
90
|
-
if (packet.guildId && player) player.emit(packet.op, packet);
|
|
91
56
|
|
|
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
57
|
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
58
|
+
#open(){
|
|
59
|
+
if (this.reconnectAttempt) {
|
|
60
|
+
clearTimeout(this.reconnectAttempt);
|
|
61
|
+
delete this.reconnectAttempt;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.resumeKey){
|
|
65
|
+
this.send({
|
|
66
|
+
op: "configureResuming",
|
|
67
|
+
key: (this.resumeKey).toString(),
|
|
68
|
+
timeout: this.resumeTimeout
|
|
69
|
+
});
|
|
70
|
+
this.manager.emit("debug",this.name,`[Web Socket] Resuming configured on Lavalink`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.manager.emit("nodeConnect", this);
|
|
74
|
+
this.isConnected = true;
|
|
75
|
+
this.manager.emit('debug', this.name, `[Web Socket] Connection ready ${this.url}`);
|
|
101
76
|
|
|
102
|
-
return this.reconnect();
|
|
103
77
|
}
|
|
104
|
-
}
|
|
105
78
|
|
|
79
|
+
#message(payload) {
|
|
80
|
+
if (Array.isArray(payload)) payload = Buffer.concat(payload);
|
|
81
|
+
else if (payload instanceof ArrayBuffer) payload = Buffer.from(payload);
|
|
106
82
|
|
|
107
|
-
|
|
108
|
-
|
|
83
|
+
const packet = JSON.parse(payload);
|
|
84
|
+
if(!packet.op) return;
|
|
85
|
+
|
|
86
|
+
if (packet.op && packet.op === "stats") {
|
|
87
|
+
this.stats = { ...packet };
|
|
88
|
+
delete this.stats.op;
|
|
89
|
+
}
|
|
90
|
+
const player = this.manager.players.get(packet.guildId);
|
|
91
|
+
if (packet.guildId && player) player.emit(packet.op, packet);
|
|
92
|
+
|
|
93
|
+
packet.node = this;
|
|
94
|
+
this.manager.emit("debug",this.name,`[Web Socket] Lavalink Node Update : ${packet.op} `)
|
|
95
|
+
this.manager.emit("raw", packet);
|
|
96
|
+
}
|
|
109
97
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
#close(event) {
|
|
99
|
+
this.manager.emit("nodeDisconnect", event, this);
|
|
100
|
+
this.manager.emit("debug",this.name,`[Web Socket] Connection with Lavalink closed with Error code : ${event||"Unknown code"}`)
|
|
101
|
+
if (event !== 1000){
|
|
102
|
+
|
|
103
|
+
return this.reconnect();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
114
106
|
|
|
115
|
-
destroy(){
|
|
116
|
-
if(!this.isConnected) return;
|
|
117
107
|
|
|
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);
|
|
108
|
+
#error(event) {
|
|
109
|
+
if (!event) return "Unknown event";
|
|
127
110
|
|
|
111
|
+
this.manager.emit("debug",this.name,`[Web Socket] Connection for Lavalink node has error code: ${event.code}`)
|
|
112
|
+
this.manager.emit("nodeError", this, event);
|
|
113
|
+
return this.reconnect();
|
|
114
|
+
}
|
|
128
115
|
|
|
129
|
-
|
|
116
|
+
destroy(){
|
|
117
|
+
if(!this.isConnected) return;
|
|
130
118
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
119
|
+
const players = this.manager.players.filter(p => p.node == this);
|
|
120
|
+
if (players.size) players.forEach(p => p.destroy());
|
|
121
|
+
this.ws.close(1000, "destroy");
|
|
134
122
|
this.ws.removeAllListeners();
|
|
135
123
|
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
|
-
}
|
|
124
|
+
this.reconnect = 1;
|
|
125
|
+
this.destroyed = true;
|
|
126
|
+
this.manager.nodes.delete(this.host)
|
|
127
|
+
this.manager.emit("nodeDestroy", this);
|
|
147
128
|
|
|
148
129
|
|
|
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
130
|
}
|
|
159
|
-
return penalties;
|
|
160
|
-
}
|
|
161
131
|
|
|
132
|
+
reconnect() {
|
|
133
|
+
this.reconnectAttempt = setTimeout(() => {
|
|
134
|
+
this.isConnected = false;
|
|
135
|
+
this.ws.removeAllListeners();
|
|
136
|
+
this.ws = null;
|
|
137
|
+
this.manager.emit("nodeReconnect", this);
|
|
138
|
+
this.connect();
|
|
139
|
+
}, this.reconnectTime);
|
|
140
|
+
}
|
|
162
141
|
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
send(payload) {
|
|
143
|
+
const data = JSON.stringify(payload);
|
|
144
|
+
this.ws.send(data, (error) => {
|
|
145
|
+
if (error) return error;
|
|
146
|
+
return null;
|
|
147
|
+
});
|
|
148
|
+
this.manager.emit("raw", data, this.name)
|
|
149
|
+
}
|
|
165
150
|
|
|
151
|
+
get penalties(){
|
|
152
|
+
let penalties = 0;
|
|
153
|
+
if (!this.isConnected) return penalties;
|
|
154
|
+
penalties += this.stats.players;
|
|
155
|
+
penalties += Math.round(Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10);
|
|
156
|
+
if (this.stats.frameStats) {
|
|
157
|
+
penalties += this.stats.frameStats.deficit;
|
|
158
|
+
penalties += this.stats.frameStats.nulled * 2;
|
|
159
|
+
}
|
|
160
|
+
return penalties;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
166
163
|
|
|
167
|
-
|
|
164
|
+
module.exports = Node;
|
|
168
165
|
|
package/src/Player.js
CHANGED
|
@@ -50,6 +50,7 @@ class Player extends EventEmitter {
|
|
|
50
50
|
this.manager.emit("playerUpdate", this, packet);
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
+
|
|
53
54
|
async play() {
|
|
54
55
|
|
|
55
56
|
if (!this.queue.length) {
|
|
@@ -72,7 +73,7 @@ class Player extends EventEmitter {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
stop() {
|
|
76
77
|
|
|
77
78
|
this.position = 0;
|
|
78
79
|
this.isConnectd = false
|
|
@@ -182,9 +183,6 @@ class Player extends EventEmitter {
|
|
|
182
183
|
return this;
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
186
|
async disconnect() {
|
|
189
187
|
if (this.voiceChannel === null) return null;
|
|
190
188
|
this.pause(true);
|
|
@@ -221,7 +219,7 @@ class Player extends EventEmitter {
|
|
|
221
219
|
|
|
222
220
|
let response = await this.manager.resolve(data);
|
|
223
221
|
|
|
224
|
-
if (!response || !response.tracks || ["LOAD_FAILED", "NO_MATCHES"].includes(response.
|
|
222
|
+
if (!response || !response.tracks || ["LOAD_FAILED", "NO_MATCHES"].includes(response.loadType)) return this.stop();
|
|
225
223
|
|
|
226
224
|
let track = response.tracks[Math.floor(Math.random() * Math.floor(response.tracks.length))];
|
|
227
225
|
|
|
@@ -306,7 +304,6 @@ class Player extends EventEmitter {
|
|
|
306
304
|
return events[data.type] || events.default;
|
|
307
305
|
}
|
|
308
306
|
|
|
309
|
-
|
|
310
307
|
}
|
|
311
308
|
|
|
312
309
|
module.exports = Player;
|
package/src/Poru.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
const { EventEmitter } = require("events");
|
|
2
|
-
const fetch = (
|
|
3
|
-
default: fetch
|
|
4
|
-
}) => fetch(...args));
|
|
2
|
+
const { fetch } = require('undici');
|
|
5
3
|
const Player = require("./Player");
|
|
6
4
|
const Node = require("./Node");
|
|
7
5
|
const Response = require("./guild/Response");
|
|
8
|
-
const config = require("./config.json")
|
|
9
6
|
const Spotify = require("./platform/Spotify")
|
|
10
7
|
const Apple = require("./platform/Apple")
|
|
11
|
-
|
|
8
|
+
const Deezer = require("./platform/Deezer")
|
|
12
9
|
class Poru extends EventEmitter {
|
|
13
10
|
constructor(client, nodes, options = {}) {
|
|
14
11
|
super();
|
|
@@ -76,27 +73,33 @@ class Poru extends EventEmitter {
|
|
|
76
73
|
const guild = client.guilds.cache.get(data.d.guild_id);
|
|
77
74
|
if (guild) guild.shard.send(data);
|
|
78
75
|
}
|
|
76
|
+
|
|
79
77
|
client.on("raw", async packet => {
|
|
80
|
-
await this
|
|
78
|
+
await this.packetUpdate(packet);
|
|
81
79
|
})
|
|
82
|
-
|
|
80
|
+
|
|
83
81
|
this._nodes.forEach((node) => this.addNode(node));
|
|
84
82
|
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
this.spotify = new Spotify(this,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
if (this.options.spotify && this.options.spotify.clientID && this.options.spotify.clientSecret) {
|
|
85
|
+
this.spotify = new Spotify(this, this.options)
|
|
86
|
+
}
|
|
87
|
+
if (this.options.apple) {
|
|
88
|
+
if (!this.options.apple.playlistLimit) {
|
|
89
|
+
throw new Error("[Poru Apple Music] playlistLimit must be provided")
|
|
90
|
+
}
|
|
91
|
+
this.apple = new Apple(this, this.options)
|
|
92
|
+
}
|
|
93
|
+
if (this.options.dezzer) {
|
|
94
|
+
if (!this.options.dezzer.playlistLimit) {
|
|
95
|
+
throw new Error("[Poru Deezer Music] playlistLimit must be provided")
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
this.deezer = new Deezer(this, this.options)
|
|
99
|
+
|
|
91
100
|
}
|
|
92
|
-
if(this.options.apple){
|
|
93
|
-
if(!this.options.apple.playlistLimit){
|
|
94
|
-
throw new Error("[Poru Apple Music] playlistLimit must be provided")
|
|
95
|
-
}
|
|
96
|
-
this.apple = new Apple(this,this.options)
|
|
97
|
-
}
|
|
98
101
|
console.log(`Thanks for using Poru`)
|
|
99
|
-
}
|
|
102
|
+
}
|
|
100
103
|
|
|
101
104
|
|
|
102
105
|
setServersUpdate(data) {
|
|
@@ -139,7 +142,7 @@ if(this.options.apple){
|
|
|
139
142
|
this.voiceStates.delete(data.guild_id);
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
packetUpdate(packet) {
|
|
143
146
|
if (!['VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE'].includes(packet.t)) return;
|
|
144
147
|
const player = this.players.get(packet.d.guild_id);
|
|
145
148
|
if (!player) return;
|
|
@@ -184,22 +187,23 @@ if(this.options.apple){
|
|
|
184
187
|
|
|
185
188
|
|
|
186
189
|
async resolve(track, source) {
|
|
187
|
-
|
|
190
|
+
|
|
188
191
|
const node = this.leastUsedNodes[0];
|
|
189
192
|
if (!node) throw new Error("No nodes are available.");
|
|
190
|
-
if(this.spotify &&
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if(this.apple && this.apple.check(track)){
|
|
193
|
+
if (this.spotify && this.spotify.check(track)) {
|
|
194
|
+
return await this.spotify.resolve(track);
|
|
195
|
+
}else if (this.apple && this.apple.check(track)) {
|
|
195
196
|
return await this.apple.resolve(track);
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
}else if (this.deezer && this.deezer.check(track)) {
|
|
198
|
+
return await this.deezer.resolve(track);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
198
202
|
|
|
199
203
|
const regex = /^https?:\/\//;
|
|
200
204
|
if (!regex.test(track)) {
|
|
201
205
|
// eslint-disable-next-line no-param-reassign
|
|
202
|
-
track = `${source || "
|
|
206
|
+
track = `${source || "ytsearch"}:${track}`;
|
|
203
207
|
}
|
|
204
208
|
const result = await this.#fetch(node, "loadtracks", `identifier=${encodeURIComponent(track)}`);
|
|
205
209
|
|
package/src/config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2
|
+
class DeezerTrack {
|
|
3
|
+
constructor(data) {
|
|
4
|
+
this.track = data.track
|
|
5
|
+
this.info = {
|
|
6
|
+
identifier: null,
|
|
7
|
+
isSeekable: data.info.isSeekable,
|
|
8
|
+
author: data.info.author,
|
|
9
|
+
length: data.info.length,
|
|
10
|
+
isStream: data.info.isStream,
|
|
11
|
+
sourceName: data.info.sourceName,
|
|
12
|
+
title: data.info.title,
|
|
13
|
+
uri: data.info.uri,
|
|
14
|
+
image: data.image || null
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async resolve(manager) {
|
|
20
|
+
|
|
21
|
+
const query = [this.info.author, this.info.title].filter((x) => !!x).join(' - ');
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const result = await manager.resolve(query);
|
|
25
|
+
if (!result || !result.tracks.length) return;
|
|
26
|
+
|
|
27
|
+
if (this.info.author) {
|
|
28
|
+
const author = [this.info.author, `${this.info.author} - Topic`];
|
|
29
|
+
const officialAudio = result.tracks.find(
|
|
30
|
+
(track) =>
|
|
31
|
+
author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) ||
|
|
32
|
+
new RegExp(`^${escapeRegExp(this.info.title)}$`, 'i').test(track.info.title),
|
|
33
|
+
);
|
|
34
|
+
if (officialAudio) {
|
|
35
|
+
this.info.identifier = officialAudio.info.identifier
|
|
36
|
+
this.track = officialAudio.track;
|
|
37
|
+
this.length = officialAudio.info.length
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (this.info.length) {
|
|
42
|
+
const sameDuration = result.tracks.find(
|
|
43
|
+
(track) =>
|
|
44
|
+
track.info.length >= (this.info.length ? this.length : 0) - 2000 &&
|
|
45
|
+
track.info.length <= (this.info.length ? this.length : 0) + 2000,
|
|
46
|
+
);
|
|
47
|
+
if (sameDuration) {
|
|
48
|
+
this.info.identifier = sameDuration.info.identifier
|
|
49
|
+
this.track = sameDuration.track;
|
|
50
|
+
this.length = officialAudio.info.length
|
|
51
|
+
return this
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.info.identifier = result.tracks[0].info.identifier
|
|
55
|
+
this.track = result.tracks[0].track;
|
|
56
|
+
this.length = officialAudio.info.length
|
|
57
|
+
return this
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = DeezerTrack;
|