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 CHANGED
@@ -87,67 +87,63 @@
87
87
 
88
88
  ## đŸ’ģ Quick Start
89
89
 
90
- ```javascript
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, Collection, GatewayDispatchEvents } = require("discord.js");
96
+ const { Client, GatewayIntentBits, Events } = require("discord.js");
103
97
 
104
98
  const client = new Client({
105
99
  intents: [
106
- "Guilds",
107
- "GuildMembers",
108
- "GuildMessages",
109
- "MessageContent",
110
- "GuildVoiceStates"
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: "yourpass",
118
- port: 233,
111
+ password: "your_password",
112
+ port: 2333,
119
113
  secure: false,
120
- name: "localhost"
114
+ name: "main-node"
121
115
  }
122
116
  ];
123
117
 
124
- const aqua = Aqua(client, nodes, {
125
- defaultSearchPlatform: "ytsearch",
126
- restVersion: "v4",
127
- autoResume: false,
128
- infiniteReconnects: true,
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("Ready!");
129
+ console.log(`Logged in as ${client.user.tag}`);
137
130
  });
138
131
 
139
-
140
- client.on("raw", (d) => {
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("messageCreate", async (message) => {
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
- if (!message.content.startsWith("!play")) return;
140
+ const query = message.content.slice(6).trim();
141
+ if (!query) return message.channel.send("Please provide a song to play.");
149
142
 
150
- const query = message.content.slice(6);
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
- const resolve = await client.aqua.resolve({ query, requester: message.member });
160
-
161
- if (resolve.loadType === 'playlist') {
162
- await message.channel.send(`Added ${resolve.tracks.length} songs from ${resolve.playlistInfo.name} playlist.`);
163
- for (const track of resolve.tracks) {
164
- player.queue.add(track)
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
- await message.channel.send(`Added **${track.info.title}** to the queue.`);
175
-
176
- if (!player.playing && !player.paused) return player.play();
177
-
178
- } else {
179
- return message.channel.send(`There were no results found for your query.`);
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.login("Yourtokenhere");
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
- [![Docs](https://img.shields.io/badge/Documentation-0099FF?style=for-the-badge&logo=readthedocs&logoColor=white)](https://toddythenoobdud.github.io/aqualink.github.io)
260
+ [![Docs](https://img.shields.io/badge/Documentation-0099FF?style=for-the-badge&logo=readthedocs&logoColor=white)](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://toddythenoobdud.github.io/aqualink.github.io)**
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 generateToken = () => {
68
- const timeStep = (Date.now() / 30000) | 0;
69
- const counter = Buffer.allocUnsafe(8);
70
- counter.writeBigUInt64BE(BigInt(timeStep), 0);
71
-
72
- const hash = crypto.createHmac('sha1', TOTP_SECRET).update(counter).digest();
73
- const offset = hash[19] & 0x0f;
74
-
75
- const binCode = (
76
- (hash[offset] & 0x7f) << 24 |
77
- hash[offset + 1] << 16 |
78
- hash[offset + 2] << 8 |
79
- hash[offset + 3]
80
- );
81
-
82
- return [
83
- (binCode % 1000000).toString().padStart(6, '0'),
84
- timeStep * 30000
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
- const spotifyAutoPlay = async (seedTrackId) => {
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
- const { tracks } = JSON.parse(recResponse);
104
- if (!tracks?.length) throw new Error("No tracks");
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,