djs-selfbot-v13 3.1.8 → 3.2.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.
Files changed (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +18 -8
  3. package/package.json +85 -71
  4. package/src/client/BaseClient.js +1 -1
  5. package/src/client/Client.js +81 -10
  6. package/src/client/actions/GuildMemberRemove.js +0 -1
  7. package/src/client/actions/GuildMemberUpdate.js +0 -1
  8. package/src/client/websocket/WebSocketShard.js +3 -3
  9. package/src/client/websocket/handlers/GUILD_CREATE.js +13 -14
  10. package/src/client/websocket/handlers/GUILD_MEMBER_ADD.js +0 -1
  11. package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_ADD.js +22 -0
  12. package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_REMOVE.js +12 -0
  13. package/src/client/websocket/handlers/READY.js +61 -21
  14. package/src/client/websocket/handlers/RELATIONSHIP_REMOVE.js +1 -1
  15. package/src/client/websocket/handlers/RELATIONSHIP_UPDATE.js +1 -1
  16. package/src/client/websocket/handlers/index.js +2 -0
  17. package/src/errors/Messages.js +1 -0
  18. package/src/index.js +4 -3
  19. package/src/managers/ChannelManager.js +1 -1
  20. package/src/managers/ClientUserSettingManager.js +2 -2
  21. package/src/managers/GuildBanManager.js +46 -0
  22. package/src/managers/GuildChannelManager.js +0 -16
  23. package/src/managers/GuildForumThreadManager.js +3 -3
  24. package/src/managers/GuildManager.js +1 -1
  25. package/src/managers/GuildMemberManager.js +11 -7
  26. package/src/managers/RelationshipManager.js +3 -3
  27. package/src/rest/APIRequest.js +13 -7
  28. package/src/structures/ClientPresence.js +9 -12
  29. package/src/structures/ClientUser.js +0 -2
  30. package/src/structures/Message.js +67 -76
  31. package/src/structures/MessagePayload.js +2 -0
  32. package/src/structures/MessagePoll.js +238 -0
  33. package/src/structures/Modal.js +11 -24
  34. package/src/structures/Presence.js +786 -128
  35. package/src/structures/User.js +35 -1
  36. package/src/structures/interfaces/TextBasedChannel.js +21 -23
  37. package/src/util/Constants.js +17 -4
  38. package/src/util/Options.js +1 -7
  39. package/src/util/Permissions.js +10 -0
  40. package/src/util/Util.js +88 -2
  41. package/typings/enums.d.ts +7 -1
  42. package/typings/index.d.ts +112 -70
  43. package/src/structures/RichPresence.js +0 -702
@@ -1,702 +0,0 @@
1
- 'use strict';
2
- const { randomUUID } = require('node:crypto');
3
- const { ActivityTypes } = require('../util/Constants');
4
- const { resolvePartialEmoji } = require('../util/Util');
5
-
6
- // eslint-disable-next-line
7
- const checkUrl = url => {
8
- try {
9
- return new URL(url);
10
- } catch {
11
- return false;
12
- }
13
- };
14
-
15
- class CustomStatus {
16
- /**
17
- * @typedef {Object} CustomStatusOptions
18
- * @property {string} [state] The state to be displayed
19
- * @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed
20
- */
21
-
22
- /**
23
- * @param {CustomStatus|CustomStatusOptions} [data={}] CustomStatus to clone or raw data
24
- * @param {Presence} [presence] The presence this activity is part of
25
- */
26
- constructor(data = {}, presence) {
27
- Object.defineProperty(this, 'presence', { value: presence });
28
- this.name = 'Custom Status';
29
- /**
30
- * The emoji to be displayed
31
- * @type {?EmojiIdentifierResolvable}
32
- */
33
- this.emoji = null;
34
- this.type = ActivityTypes.CUSTOM;
35
- /**
36
- * The state to be displayed
37
- * @type {?string}
38
- */
39
- this.state = null;
40
- this.setup(data);
41
- }
42
- /**
43
- * Sets the status from a JSON object
44
- * @param {CustomStatus|CustomStatusOptions} data CustomStatus to clone or raw data
45
- * @private
46
- */
47
- setup(data) {
48
- this.emoji = data.emoji ? resolvePartialEmoji(data.emoji) : null;
49
- this.state = data.state;
50
- }
51
- /**
52
- * Set the emoji of this activity
53
- * @param {EmojiIdentifierResolvable} emoji The emoji to be displayed
54
- * @returns {CustomStatus}
55
- */
56
- setEmoji(emoji) {
57
- this.emoji = resolvePartialEmoji(emoji);
58
- return this;
59
- }
60
- /**
61
- * Set state of this activity
62
- * @param {string | null} state The state to be displayed
63
- * @returns {CustomStatus}
64
- */
65
- setState(state) {
66
- if (typeof state == 'string' && state.length > 128) throw new Error('State must be less than 128 characters');
67
- this.state = state;
68
- return this;
69
- }
70
-
71
- /**
72
- * Returns an object that can be used to set the status
73
- * @returns {CustomStatus}
74
- */
75
- toJSON() {
76
- if (!this.emoji & !this.state) throw new Error('CustomStatus must have at least one of emoji or state');
77
- return {
78
- name: this.name,
79
- emoji: this.emoji,
80
- type: this.type,
81
- state: this.state,
82
- };
83
- }
84
-
85
- /**
86
- * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
87
- * @returns {string}
88
- */
89
- toString() {
90
- return this.name;
91
- }
92
-
93
- _clone() {
94
- return Object.assign(Object.create(this), this);
95
- }
96
- }
97
-
98
- class RichPresence {
99
- /**
100
- * @param {Client} [client] Discord client
101
- * @param {RichPresence} [data={}] RichPresence to clone or raw data
102
- * @param {boolean} [IPC=false] Whether to use IPC (RPC for Discord Apps)
103
- * @param {Presence} [presence] The presence this activity is part of
104
- */
105
- constructor(client = {}, data = {}, IPC = false, presence) {
106
- Object.defineProperty(this, 'client', { value: client });
107
- Object.defineProperty(this, 'presence', { value: presence });
108
- /**
109
- * The activity's name
110
- * @type {string}
111
- */
112
- this.name = null;
113
- /**
114
- * The activity status's type
115
- * @type {ActivityType}
116
- */
117
- this.type = ActivityTypes.PLAYING;
118
- /**
119
- * If the activity is being streamed, a link to the stream
120
- * @type {?string}
121
- */
122
- this.url = null;
123
- /**
124
- * The id of the application associated with this activity
125
- * @type {?Snowflake}
126
- */
127
- this.application_id = null;
128
- /**
129
- * State of the activity
130
- * @type {?string}
131
- */
132
- this.state = null;
133
- /**
134
- * Details about the activity
135
- * @type {?string}
136
- */
137
- this.details = null;
138
- /**
139
- * Party of the activity
140
- * @type {?ActivityParty}
141
- */
142
- this.party = null;
143
- /**
144
- * Timestamps for the activity
145
- * @type {?ActivityTimestamps}
146
- */
147
- this.timestamps = null;
148
- /**
149
- * Assets for rich presence
150
- * @type {?RichPresenceAssets}
151
- */
152
- this.assets = null;
153
- /**
154
- * The labels of the buttons of this rich presence
155
- * @type {string[]}
156
- */
157
- this.buttons = null;
158
-
159
- this.ipc = IPC;
160
-
161
- this.setup(data);
162
- }
163
- /**
164
- * Sets the status from a JSON object
165
- * @param {RichPresence} data data
166
- * @private
167
- */
168
- setup(data) {
169
- this.name = data.name;
170
- this.type = typeof data.type != 'number' ? ActivityTypes[data.type?.toUpperCase()] : data.type;
171
- this.application_id = data.application_id;
172
- this.url = data.url;
173
- this.state = data.state;
174
- this.details = data.details;
175
- this.party = data.party;
176
- this.timestamps = data.timestamps;
177
- this.created_at = data.created_at;
178
- this.secrets = data.secrets;
179
- this.assets = data.assets;
180
- this.buttons = data.buttons;
181
- this.metadata = data.metadata;
182
- }
183
- /**
184
- * @typedef {string} RichPresenceImage
185
- * Support:
186
- * - cdn.discordapp.com
187
- * - media.discordapp.net
188
- * - Asset ID (From https://discord.com/api/v9/oauth2/applications/:id/assets)
189
- * - ExternalAssets (mp:external/)
190
- */
191
- /**
192
- * Set the large image of this activity
193
- * @param {?RichPresenceImage} image The large image asset's id
194
- * @returns {RichPresence}
195
- */
196
- setAssetsLargeImage(image) {
197
- if (!(this.assets instanceof Object)) this.assets = {};
198
- if (typeof image != 'string') {
199
- image = null;
200
- } else if (['http:', 'https:'].includes(checkUrl(image)?.protocol)) {
201
- // Discord URL:
202
- image = image
203
- .replace('https://cdn.discordapp.com/', 'mp:')
204
- .replace('http://cdn.discordapp.com/', 'mp:')
205
- .replace('https://media.discordapp.net/', 'mp:')
206
- .replace('http://media.discordapp.net/', 'mp:');
207
- //
208
- if (!image.startsWith('mp:') && !this.ipc) {
209
- throw new Error('INVALID_URL');
210
- }
211
- } else if (/^[0-9]{17,19}$/.test(image)) {
212
- // ID Assets
213
- } else if (image.startsWith('mp:') || image.startsWith('youtube:') || image.startsWith('spotify:')) {
214
- // Image
215
- } else if (image.startsWith('external/')) {
216
- image = `mp:${image}`;
217
- }
218
- this.assets.large_image = image;
219
- return this;
220
- }
221
- /**
222
- * Set the small image of this activity
223
- * @param {?RichPresenceImage} image The small image asset's id
224
- * @returns {RichPresence}
225
- */
226
- setAssetsSmallImage(image) {
227
- if (!(this.assets instanceof Object)) this.assets = {};
228
- if (typeof image != 'string') {
229
- image = null;
230
- } else if (['http:', 'https:'].includes(checkUrl(image)?.protocol)) {
231
- // Discord URL:
232
- image = image
233
- .replace('https://cdn.discordapp.com/', 'mp:')
234
- .replace('http://cdn.discordapp.com/', 'mp:')
235
- .replace('https://media.discordapp.net/', 'mp:')
236
- .replace('http://media.discordapp.net/', 'mp:');
237
- //
238
- if (!image.startsWith('mp:') && !this.ipc) {
239
- throw new Error('INVALID_URL');
240
- }
241
- } else if (/^[0-9]{17,19}$/.test(image)) {
242
- // ID Assets
243
- } else if (image.startsWith('mp:') || image.startsWith('youtube:') || image.startsWith('spotify:')) {
244
- // Image
245
- } else if (image.startsWith('external/')) {
246
- image = `mp:${image}`;
247
- }
248
- this.assets.small_image = image;
249
- return this;
250
- }
251
- /**
252
- * Hover text for the large image
253
- * @param {string} text Assets text
254
- * @returns {RichPresence}
255
- */
256
- setAssetsLargeText(text) {
257
- if (typeof this.assets !== 'object') this.assets = {};
258
- this.assets.large_text = text;
259
- return this;
260
- }
261
- /**
262
- * Hover text for the small image
263
- * @param {string} text Assets text
264
- * @returns {RichPresence}
265
- */
266
- setAssetsSmallText(text) {
267
- if (typeof this.assets !== 'object') this.assets = {};
268
- this.assets.small_text = text;
269
- return this;
270
- }
271
- /**
272
- * Set the name of the activity
273
- * @param {?string} name The activity's name
274
- * @returns {RichPresence}
275
- */
276
- setName(name) {
277
- this.name = name;
278
- return this;
279
- }
280
- /**
281
- * If the activity is being streamed, a link to the stream
282
- * @param {?string} url URL of the stream
283
- * @returns {RichPresence}
284
- */
285
- setURL(url) {
286
- if (typeof url == 'string' && !checkUrl(url)) throw new Error('URL must be a valid URL');
287
- if (typeof url != 'string') url = null;
288
- this.url = url;
289
- return this;
290
- }
291
- /**
292
- * The activity status's type
293
- * @param {?ActivityTypes} type The type of activity
294
- * @returns {RichPresence}
295
- */
296
- setType(type) {
297
- this.type = ActivityTypes[type];
298
- if (typeof this.type == 'string') this.type = ActivityTypes[this.type];
299
- if (typeof this.type != 'number') throw new Error('Type must be a valid ActivityType');
300
- return this;
301
- }
302
- /**
303
- * Set the application id of this activity
304
- * @param {?Snowflake} id Bot's id
305
- * @returns {RichPresence}
306
- */
307
- setApplicationId(id) {
308
- this.application_id = id;
309
- return this;
310
- }
311
- /**
312
- * Set the state of the activity
313
- * @param {?string} state The state of the activity
314
- * @returns {RichPresence}
315
- */
316
- setState(state) {
317
- this.state = state;
318
- return this;
319
- }
320
- /**
321
- * Set the details of the activity
322
- * @param {?string} details The details of the activity
323
- * @returns {RichPresence}
324
- */
325
- setDetails(details) {
326
- this.details = details;
327
- return this;
328
- }
329
- /**
330
- * @typedef {Object} RichParty
331
- * @property {string} id The id of the party
332
- * @property {number} max The maximum number of members in the party
333
- * @property {number} current The current number of members in the party
334
- */
335
- /**
336
- * Set the party of this activity
337
- * @param {?RichParty} party The party to be displayed
338
- * @returns {RichPresence}
339
- */
340
- setParty(party) {
341
- if (typeof party == 'object') {
342
- if (!party.max || typeof party.max != 'number') throw new Error('Party must have max number');
343
- if (!party.current || typeof party.current != 'number') throw new Error('Party must have current');
344
- if (party.current > party.max) throw new Error('Party current must be less than max number');
345
- if (!party.id || typeof party.id != 'string') party.id = randomUUID();
346
- this.party = {
347
- size: [party.current, party.max],
348
- id: party.id,
349
- };
350
- } else {
351
- this.party = null;
352
- }
353
- return this;
354
- }
355
- /**
356
- * Sets the start timestamp of the activity
357
- * @param {?number} timestamp The timestamp of the start of the activity
358
- * @returns {RichPresence}
359
- */
360
- setStartTimestamp(timestamp) {
361
- if (!this.timestamps) this.timestamps = {};
362
- this.timestamps.start = timestamp;
363
- return this;
364
- }
365
- /**
366
- * Sets the end timestamp of the activity
367
- * @param {?number} timestamp The timestamp of the end of the activity
368
- * @returns {RichPresence}
369
- */
370
- setEndTimestamp(timestamp) {
371
- if (!this.timestamps) this.timestamps = {};
372
- this.timestamps.end = timestamp;
373
- return this;
374
- }
375
- /**
376
- * @typedef {object} RichButton
377
- * @property {string} name The name of the button
378
- * @property {string} url The url of the button
379
- */
380
- /**
381
- * Set the buttons of the rich presence
382
- * @param {...?RichButton} button A list of buttons to set
383
- * @returns {RichPresence}
384
- */
385
- setButtons(...button) {
386
- if (button.length == 0) {
387
- this.buttons = null;
388
- delete this.metadata;
389
- return this;
390
- } else if (button.length > 2) {
391
- throw new Error('RichPresence can only have up to 2 buttons');
392
- }
393
- this.buttons = [];
394
- this.metadata = {
395
- button_urls: [],
396
- };
397
- button.flat(2).forEach(b => {
398
- if (b.name && b.url) {
399
- this.buttons.push(b.name);
400
- if (!checkUrl(b.url)) throw new Error('Button url must be a valid url');
401
- this.metadata.button_urls.push(b.url);
402
- } else {
403
- throw new Error('Button must have name and url');
404
- }
405
- });
406
- return this;
407
- }
408
- /**
409
- * Add a button to the rich presence
410
- * @param {string} name The name of the button
411
- * @param {string} url The url of the button
412
- * @returns {RichPresence}
413
- */
414
- addButton(name, url) {
415
- if (!name || !url) {
416
- throw new Error('Button must have name and url');
417
- }
418
- if (typeof name !== 'string') throw new Error('Button name must be a string');
419
- if (!checkUrl(url)) throw new Error('Button url must be a valid url');
420
- if (!this.buttons) {
421
- this.buttons = [];
422
- this.metadata = {
423
- button_urls: [],
424
- };
425
- }
426
- this.buttons.push(name);
427
- this.metadata.button_urls.push(url);
428
- return this;
429
- }
430
- /**
431
- * Convert the rich presence to a JSON object
432
- * @returns {Object}
433
- */
434
- toJSON() {
435
- /**
436
- * * Verify Timestamps
437
- */
438
- if (this.timestamps?.start || this.timestamps?.end) {
439
- if (this.timestamps?.start instanceof Date) {
440
- this.timestamps.start = Math.round(this.timestamps?.start.getTime());
441
- }
442
- if (this.timestamps.end instanceof Date) {
443
- this.timestamps.end = Math.round(this.timestamps.end.getTime());
444
- }
445
- if (this.timestamps.start > 2147483647000) {
446
- throw new RangeError('timestamps.start must fit into a unix timestamp');
447
- }
448
- if (this.timestamps.end > 2147483647000) {
449
- throw new RangeError('timestamps.end must fit into a unix timestamp');
450
- }
451
- }
452
- const obj = {
453
- name: this.name,
454
- type: this.type || 0, // PLAYING
455
- application_id: this.application_id,
456
- url: this.url,
457
- state: this.state,
458
- details: this.details,
459
- party: this.party,
460
- timestamps: this.timestamps || {},
461
- secrets: this.secrets,
462
- assets: this.assets || {},
463
- buttons: this.buttons,
464
- metadata: this.metadata,
465
- };
466
- Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]);
467
- if (!this.ipc) {
468
- return obj;
469
- } else {
470
- delete obj.application_id;
471
- delete obj.name;
472
- delete obj.url;
473
- obj.type = 0;
474
- if (obj.buttons?.length) {
475
- obj.buttons = obj.buttons.map((b, i) => ({ label: b, url: obj.metadata.button_urls[i] }));
476
- delete obj.metadata;
477
- }
478
- return obj;
479
- }
480
- }
481
-
482
- /**
483
- * Get Assets from a RichPresence (Util)
484
- * @param {Client} client Discord Client
485
- * @param {Snowflake} applicationId Application id
486
- * @param {string} image1 URL image 1 (not from Discord)
487
- * @param {string} image2 URL image 2 (not from Discord)
488
- * @returns {ExternalAssets[]}
489
- */
490
- static async getExternal(client, applicationId, image1 = '', image2 = '') {
491
- const checkURL = url => {
492
- try {
493
- // eslint-disable-next-line no-new
494
- new URL(url);
495
- return true;
496
- } catch (e) {
497
- return false;
498
- }
499
- };
500
- if (!client || !client.token || !client.api) throw new Error('Client must be set');
501
- // Check if applicationId is discord snowflake (17 , 18, 19 numbers)
502
- if (!/^[0-9]{17,19}$/.test(applicationId)) {
503
- throw new Error('Application id must be a Discord Snowflake');
504
- }
505
- // Check if large_image is a valid url
506
- if (image1 && image1.length > 0 && !checkURL(image1)) {
507
- throw new Error('Image 1 must be a valid url');
508
- }
509
- // Check if small_image is a valid url
510
- if (image2 && image2.length > 0 && !checkURL(image2)) {
511
- throw new Error('Image 2 must be a valid url');
512
- }
513
- const data_ = [];
514
- if (image1) data_.push(image1);
515
- if (image2) data_.push(image2);
516
- const res = await client.api.applications[applicationId]['external-assets'].post({
517
- data: {
518
- urls: data_,
519
- },
520
- });
521
- return res;
522
- }
523
-
524
- /**
525
- * When concatenated with a string, this automatically returns the activities' name instead of the Activity object.
526
- * @returns {string}
527
- */
528
- toString() {
529
- return this.name;
530
- }
531
-
532
- _clone() {
533
- return Object.assign(Object.create(this), this);
534
- }
535
- }
536
-
537
- /**
538
- * @extends {RichPresence}
539
- */
540
- class SpotifyRPC extends RichPresence {
541
- /**
542
- * Create a new RichPresence (Spotify style)
543
- * @param {Client} client Discord Client
544
- * @param {SpotifyRPC} options Options for the Spotify RPC
545
- * @param {Presence} presence Presence
546
- */
547
- constructor(client, options = {}, presence) {
548
- if (!client) throw new Error('Client must be set');
549
- super(client, options, false, presence);
550
- this.setup(options);
551
- }
552
- /**
553
- * Sets the status from a JSON object
554
- * @param {SpotifyRPC} options data
555
- * @private
556
- */
557
- setup(options) {
558
- this.name = options.name || 'Spotify';
559
-
560
- this.type = ActivityTypes.LISTENING;
561
-
562
- this.party = {
563
- id: `spotify:${this.client.user.id}`,
564
- };
565
- /**
566
- * The Spotify song's id
567
- * @type {?string}
568
- */
569
- this.sync_id = options.sync_id;
570
- /**
571
- * The activity's id
572
- * @type {string}
573
- */
574
- this.id = 'spotify:1';
575
- /**
576
- * Flags that describe the activity
577
- * @type {ActivityFlags}
578
- */
579
- this.flags = 48; // Sync + Play (ActivityFlags)
580
-
581
- /**
582
- * @typedef {Object} SpotifyMetadata
583
- * @property {string} album_id Album id
584
- * @property {Array<string>} artist_ids Artist ids
585
- */
586
-
587
- /**
588
- * Spotify metadata
589
- * @type {SpotifyMetadata}
590
- */
591
- this.metadata = {
592
- album_id: options.metadata?.album_id || null,
593
- artist_ids: options.metadata?.artist_ids || [],
594
- context_uri: null,
595
- };
596
- }
597
-
598
- /**
599
- * Set the large image of this activity
600
- * @param {?string} image Spotify song's image ID
601
- * @returns {SpotifyRPC}
602
- */
603
- setAssetsLargeImage(image) {
604
- if (image.startsWith('spotify:')) image = image.replace('spotify:', '');
605
- super.setAssetsLargeImage(`spotify:${image}`);
606
- return this;
607
- }
608
-
609
- /**
610
- * Set the small image of this activity
611
- * @param {?string} image Spotify song's image ID
612
- * @returns {RichPresence}
613
- */
614
- setAssetsSmallImage(image) {
615
- if (image.startsWith('spotify:')) image = image.replace('spotify:', '');
616
- super.setAssetsSmallImage(`spotify:${image}`);
617
- return this;
618
- }
619
-
620
- /**
621
- * Set Spotify song id to sync with
622
- * @param {string} id Song id
623
- * @returns {SpotifyRPC}
624
- */
625
- setSongId(id) {
626
- this.sync_id = id;
627
- return this;
628
- }
629
-
630
- /**
631
- * Add the artist id
632
- * @param {string} id Artist id
633
- * @returns {SpotifyRPC}
634
- */
635
- addArtistId(id) {
636
- if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
637
- this.metadata.artist_ids.push(id);
638
- return this;
639
- }
640
-
641
- /**
642
- * Set the artist ids
643
- * @param {string | Array<string>} ids Artist ids
644
- * @returns {SpotifyRPC}
645
- */
646
- setArtistIds(...ids) {
647
- if (!ids?.length) {
648
- this.metadata.artist_ids = [];
649
- return this;
650
- }
651
- if (!this.metadata.artist_ids) this.metadata.artist_ids = [];
652
- ids.flat(2).forEach(id => this.metadata.artist_ids.push(id));
653
- return this;
654
- }
655
-
656
- /**
657
- * Set the album id
658
- * @param {string} id Album id
659
- * @returns {SpotifyRPC}
660
- */
661
- setAlbumId(id) {
662
- this.metadata.album_id = id;
663
- this.metadata.context_uri = `spotify:album:${id}`;
664
- return this;
665
- }
666
-
667
- /**
668
- * Convert the rich presence to a JSON object
669
- * @returns {SpotifyRPC}
670
- */
671
- toJSON() {
672
- if (!this.sync_id) throw new Error('Song id is required');
673
- const obj = {
674
- name: this.name,
675
- type: this.type,
676
- application_id: this.application_id,
677
- url: this.url,
678
- state: this.state,
679
- details: this.details,
680
- party: this.party,
681
- timestamps: this.timestamps || {},
682
- assets: this.assets || {},
683
- sync_id: this.sync_id,
684
- flags: this.flags,
685
- metadata: this.metadata,
686
- };
687
- Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]);
688
- return obj;
689
- }
690
- }
691
-
692
- /**
693
- * @typedef {Object} ExternalAssets
694
- * @property {?string} url Orginal url of the image
695
- * @property {?string} external_asset_path Proxy url of the image (Using to RPC)
696
- */
697
-
698
- module.exports = {
699
- CustomStatus,
700
- RichPresence,
701
- SpotifyRPC,
702
- };