aqualink 2.9.0 â 2.9.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/README.md +92 -58
- package/build/handlers/autoplay.js +39 -44
- package/build/structures/Aqua.js +164 -377
- package/build/structures/Connection.js +57 -64
- package/build/structures/Node.js +54 -78
- package/build/structures/Player.js +212 -361
- package/build/structures/Rest.js +73 -45
- package/build/structures/Track.js +55 -126
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -87,67 +87,63 @@
|
|
|
87
87
|
|
|
88
88
|
## đģ Quick Start
|
|
89
89
|
|
|
90
|
-
```
|
|
91
|
-
npm install aqualink
|
|
92
|
-
|
|
93
|
-
// If you're using Module, use this:
|
|
94
|
-
// import { createRequire } from 'module';
|
|
95
|
-
// const require = createRequire(import.meta.url);
|
|
96
|
-
|
|
97
|
-
//const { Aqua } = require('aqualink');
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
```bash
|
|
91
|
+
npm install aqualink discord.js
|
|
92
|
+
```
|
|
100
93
|
|
|
94
|
+
```javascript
|
|
101
95
|
const { Aqua } = require("aqualink");
|
|
102
|
-
const { Client,
|
|
96
|
+
const { Client, GatewayIntentBits, Events } = require("discord.js");
|
|
103
97
|
|
|
104
98
|
const client = new Client({
|
|
105
99
|
intents: [
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
GatewayIntentBits.Guilds,
|
|
101
|
+
GatewayIntentBits.GuildMembers,
|
|
102
|
+
GatewayIntentBits.GuildMessages,
|
|
103
|
+
GatewayIntentBits.MessageContent,
|
|
104
|
+
GatewayIntentBits.GuildVoiceStates
|
|
111
105
|
]
|
|
112
106
|
});
|
|
113
107
|
|
|
114
108
|
const nodes = [
|
|
115
109
|
{
|
|
116
110
|
host: "127.0.0.1",
|
|
117
|
-
password: "
|
|
118
|
-
port:
|
|
111
|
+
password: "your_password",
|
|
112
|
+
port: 2333,
|
|
119
113
|
secure: false,
|
|
120
|
-
name: "
|
|
114
|
+
name: "main-node"
|
|
121
115
|
}
|
|
122
116
|
];
|
|
123
117
|
|
|
124
|
-
const aqua = Aqua(client, nodes, {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
const aqua = new Aqua(client, nodes, {
|
|
119
|
+
defaultSearchPlatform: "ytsearch",
|
|
120
|
+
restVersion: "v4",
|
|
121
|
+
autoResume: true,
|
|
122
|
+
infiniteReconnects: true
|
|
129
123
|
});
|
|
130
124
|
|
|
131
125
|
client.aqua = aqua;
|
|
132
126
|
|
|
133
|
-
|
|
134
|
-
client.once("ready", () => {
|
|
127
|
+
client.once(Events.Ready, () => {
|
|
135
128
|
client.aqua.init(client.user.id);
|
|
136
|
-
console.log(
|
|
129
|
+
console.log(`Logged in as ${client.user.tag}`);
|
|
137
130
|
});
|
|
138
131
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (![GatewayDispatchEvents.VoiceStateUpdate, GatewayDispatchEvents.VoiceServerUpdate,].includes(d.t)) return;
|
|
132
|
+
client.on(Events.Raw, (d) => {
|
|
133
|
+
if (![Events.VoiceStateUpdate, Events.VoiceServerUpdate].includes(d.t)) return;
|
|
142
134
|
client.aqua.updateVoiceState(d);
|
|
143
135
|
});
|
|
144
136
|
|
|
145
|
-
client.on(
|
|
146
|
-
if (message.author.bot) return;
|
|
137
|
+
client.on(Events.MessageCreate, async (message) => {
|
|
138
|
+
if (message.author.bot || !message.content.startsWith("!play")) return;
|
|
147
139
|
|
|
148
|
-
|
|
140
|
+
const query = message.content.slice(6).trim();
|
|
141
|
+
if (!query) return message.channel.send("Please provide a song to play.");
|
|
149
142
|
|
|
150
|
-
|
|
143
|
+
// Check if user is in a voice channel
|
|
144
|
+
if (!message.member.voice.channel) {
|
|
145
|
+
return message.channel.send("You need to be in a voice channel to play music!");
|
|
146
|
+
}
|
|
151
147
|
|
|
152
148
|
const player = client.aqua.createConnection({
|
|
153
149
|
guildId: message.guild.id,
|
|
@@ -156,38 +152,76 @@ client.on("messageCreate", async (message) => {
|
|
|
156
152
|
deaf: true,
|
|
157
153
|
});
|
|
158
154
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
try {
|
|
156
|
+
const resolve = await client.aqua.resolve({ query, requester: message.member });
|
|
157
|
+
const { loadType, tracks, playlistInfo } = resolve;
|
|
158
|
+
|
|
159
|
+
if (loadType === 'playlist') {
|
|
160
|
+
for (const track of tracks) {
|
|
161
|
+
player.queue.add(track);
|
|
162
|
+
}
|
|
163
|
+
message.channel.send(`Added ${tracks.length} songs from ${playlistInfo.name}.`);
|
|
164
|
+
} else if (loadType === 'search' || loadType === 'track') {
|
|
165
|
+
const track = tracks[0];
|
|
166
|
+
player.queue.add(track);
|
|
167
|
+
message.channel.send(`Added **${track.title}** to the queue.`);
|
|
168
|
+
} else {
|
|
169
|
+
return message.channel.send("No results found.");
|
|
165
170
|
}
|
|
166
|
-
if (!player.playing && !player.paused) return player.play();
|
|
167
|
-
|
|
168
|
-
} else if (resolve.loadType === 'search' || resolve.loadType === 'track') {
|
|
169
|
-
const track = resolve.tracks.shift();
|
|
170
|
-
track.info.requester = message.member;
|
|
171
|
-
|
|
172
|
-
player.queue.add(track);
|
|
173
171
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
172
|
+
if (!player.playing && !player.paused) {
|
|
173
|
+
player.play();
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("Playback error:", error);
|
|
177
|
+
message.channel.send("An error occurred while trying to play the song.");
|
|
180
178
|
}
|
|
181
179
|
});
|
|
182
180
|
|
|
183
181
|
client.aqua.on("nodeConnect", (node) => {
|
|
184
182
|
console.log(`Node connected: ${node.name}`);
|
|
185
183
|
});
|
|
184
|
+
|
|
186
185
|
client.aqua.on("nodeError", (node, error) => {
|
|
187
186
|
console.log(`Node "${node.name}" encountered an error: ${error.message}.`);
|
|
188
187
|
});
|
|
189
188
|
|
|
190
|
-
client.
|
|
189
|
+
client.aqua.on('trackStart', (player, track) => {
|
|
190
|
+
const channel = client.channels.cache.get(player.textChannel);
|
|
191
|
+
if (channel) channel.send(`Now playing: **${track.title}**`);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
client.aqua.on('queueEnd', (player) => {
|
|
195
|
+
const channel = client.channels.cache.get(player.textChannel);
|
|
196
|
+
if (channel) channel.send('The queue has ended.');
|
|
197
|
+
player.destroy();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
client.login("YOUR_DISCORD_BOT_TOKEN");
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Additional Commands You Can Add:
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
client.on(Events.MessageCreate, async (message) => {
|
|
207
|
+
if (message.content === "!skip") {
|
|
208
|
+
const player = client.aqua.players.get(message.guild.id);
|
|
209
|
+
if (player) {
|
|
210
|
+
player.skip();
|
|
211
|
+
message.channel.send("âī¸ Skipped current track!");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
client.on(Events.MessageCreate, async (message) => {
|
|
217
|
+
if (message.content === "!stop") {
|
|
218
|
+
const player = client.aqua.players.get(message.guild.id);
|
|
219
|
+
if (player) {
|
|
220
|
+
player.destroy();
|
|
221
|
+
message.channel.send("âšī¸ Stopped playback and cleared queue!");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
191
225
|
```
|
|
192
226
|
|
|
193
227
|
## đ Featured Projects
|
|
@@ -223,7 +257,7 @@ client.login("Yourtokenhere");
|
|
|
223
257
|
|
|
224
258
|
For detailed usage, API references, and examples, check out our official documentation:
|
|
225
259
|
|
|
226
|
-
[](https://
|
|
260
|
+
[](https://roddynnn.github.io/)
|
|
227
261
|
|
|
228
262
|
đ **Get Started Quickly**
|
|
229
263
|
- Installation guide
|
|
@@ -231,7 +265,7 @@ For detailed usage, API references, and examples, check out our official documen
|
|
|
231
265
|
- Advanced features
|
|
232
266
|
- Troubleshooting
|
|
233
267
|
|
|
234
|
-
đ Visit: **[Aqualink Docs](https://
|
|
268
|
+
đ Visit: **[Aqualink Docs](https://roddynnn.github.io/)**
|
|
235
269
|
|
|
236
270
|
## đ Premium Bots Using Aqualink
|
|
237
271
|
|
|
@@ -313,7 +347,7 @@ For detailed usage, API references, and examples, check out our official documen
|
|
|
313
347
|
<sub><b>SoulDevs</b></sub>
|
|
314
348
|
</a>
|
|
315
349
|
<br />
|
|
316
|
-
<a href="#code-SoulDevs title="Code">đģ</a>
|
|
350
|
+
<a href="#code-SoulDevs" title="Code">đģ</a>
|
|
317
351
|
</td>
|
|
318
352
|
</tr>
|
|
319
353
|
</tbody>
|
|
@@ -356,4 +390,4 @@ Join our thriving community of developers and bot creators!
|
|
|
356
390
|
|
|
357
391
|
<sub>Built with đ by the Aqualink Team</sub>
|
|
358
392
|
|
|
359
|
-
</div>
|
|
393
|
+
</div>
|
|
@@ -9,7 +9,6 @@ const agent = new https.Agent({
|
|
|
9
9
|
freeSocketTimeout: 4000
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
const TOTP_SECRET = Buffer.from("5507145853487499592248630329347", 'utf8');
|
|
13
12
|
|
|
14
13
|
const SOUNDCLOUD_REGEX = /<a\s+itemprop="url"\s+href="(\/[^"]+)"/g;
|
|
15
14
|
|
|
@@ -48,7 +47,7 @@ const fastFetch = (url, options = {}) => {
|
|
|
48
47
|
const soundAutoPlay = async (baseUrl) => {
|
|
49
48
|
try {
|
|
50
49
|
const html = await fastFetch(`${baseUrl}/recommended`);
|
|
51
|
-
|
|
50
|
+
|
|
52
51
|
const links = [];
|
|
53
52
|
let match;
|
|
54
53
|
while ((match = SOUNDCLOUD_REGEX.exec(html)) && links.length < 50) {
|
|
@@ -64,51 +63,47 @@ const soundAutoPlay = async (baseUrl) => {
|
|
|
64
63
|
}
|
|
65
64
|
};
|
|
66
65
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
66
|
+
const spotifyAutoPlay = async (seed, player, requester, excludedIdentifiers = []) => {
|
|
67
|
+
try {
|
|
68
|
+
const { trackId, artistIds } = seed
|
|
69
|
+
if (!trackId) return null
|
|
70
|
+
|
|
71
|
+
const prevIdentifier = player.current?.identifier
|
|
72
|
+
let seedQuery = `seed_tracks=${trackId}`
|
|
73
|
+
if (artistIds) seedQuery += `&seed_artists=${artistIds}`
|
|
74
|
+
console.log('Seed query:', seedQuery)
|
|
75
|
+
|
|
76
|
+
const response = await player.aqua.resolve({
|
|
77
|
+
query: seedQuery,
|
|
78
|
+
source: 'spsearch',
|
|
79
|
+
requester
|
|
80
|
+
})
|
|
81
|
+
const candidates = response?.tracks || []
|
|
82
|
+
|
|
83
|
+
const seenIds = new Set(excludedIdentifiers)
|
|
84
|
+
if (prevIdentifier) seenIds.add(prevIdentifier)
|
|
85
|
+
|
|
86
|
+
const result = []
|
|
87
|
+
for (const track of candidates) {
|
|
88
|
+
const { identifier } = track
|
|
89
|
+
if (seenIds.has(identifier)) continue
|
|
90
|
+
seenIds.add(identifier)
|
|
91
|
+
track.pluginInfo = {
|
|
92
|
+
...(track.pluginInfo || {}),
|
|
93
|
+
clientData: { fromAutoplay: true }
|
|
94
|
+
}
|
|
95
|
+
result.push(track)
|
|
96
|
+
if (result.length === 5) break
|
|
97
|
+
}
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
const [totp, ts] = generateToken();
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
const tokenUrl = `https://open.spotify.com/api/token?reason=init&productType=embed&totp=${totp}&totpVer=5&ts=${ts}`;
|
|
93
|
-
const tokenResponse = await fastFetch(tokenUrl);
|
|
94
|
-
const { accessToken } = JSON.parse(tokenResponse);
|
|
95
|
-
|
|
96
|
-
if (!accessToken) throw new Error("No access token");
|
|
97
|
-
|
|
98
|
-
const recUrl = `https://api.spotify.com/v1/recommendations?limit=10&seed_tracks=${seedTrackId}`;
|
|
99
|
-
const recResponse = await fastFetch(recUrl, {
|
|
100
|
-
headers: { Authorization: `Bearer ${accessToken}` }
|
|
101
|
-
});
|
|
99
|
+
return result
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error('Spotify autoplay error:', err)
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
105
106
|
|
|
106
|
-
return tracks[Math.random() * tracks.length | 0].id;
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error("Spotify error:", err.message);
|
|
109
|
-
throw err;
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
107
|
|
|
113
108
|
module.exports = {
|
|
114
109
|
scAutoPlay: soundAutoPlay,
|