djs-selfbot-v13 3.7.14 → 3.7.16

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "djs-selfbot-v13",
3
- "version": "3.7.14",
3
+ "version": "3.7.16",
4
4
  "description": "An unofficial discord.js fork for creating selfbots",
5
5
  "main": "./src/index.js",
6
6
  "types": "./typings/index.d.ts",
@@ -16,9 +16,7 @@ class APIRequest {
16
16
  this.route = options.route;
17
17
  this.options = options;
18
18
  this.retries = 0;
19
-
20
19
  this.fullUserAgent = this.client.options.http.headers['User-Agent'];
21
-
22
20
  this.client.options.ws.properties.browser_user_agent = this.fullUserAgent;
23
21
 
24
22
  let queryString = '';
@@ -223,19 +223,8 @@ class BaseGuildVoiceChannel extends GuildChannel {
223
223
  return this.edit({ videoQualityMode }, reason);
224
224
  }
225
225
 
226
- // These are here only for documentation purposes - they are implemented by TextBasedChannel
227
- /* eslint-disable no-empty-function */
228
- get lastMessage() {}
229
- send() {}
230
- sendTyping() {}
231
- createMessageCollector() {}
232
- awaitMessages() {}
233
- fetchWebhooks() {}
234
- createWebhook() {}
235
- setRateLimitPerUser() {}
236
- setNSFW() {}
237
226
  }
238
227
 
239
228
  TextBasedChannel.applyToClass(BaseGuildVoiceChannel, true, ['lastPinAt']);
240
229
 
241
- module.exports = BaseGuildVoiceChannel;
230
+ module.exports = BaseGuildVoiceChannel;
@@ -41,20 +41,44 @@ class ClientPresence extends Presence {
41
41
  since: typeof since === 'number' && !Number.isNaN(since) ? this.since : 0,
42
42
  status: status ?? this.status,
43
43
  };
44
+
45
+ // Récupère le custom status actuellement en cache pour ne pas l'écraser
46
+ // lors d'un setActivity / setStatus qui ne touche pas au custom status
47
+ const existingCustom = this.activities?.find(a =>
48
+ CustomStatusActivityTypes.includes(a.type),
49
+ );
50
+
44
51
  if (activities?.length) {
52
+ let hasCustom = false;
45
53
  for (const [i, activity] of activities.entries()) {
46
54
  if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string');
47
55
 
48
56
  activity.type ??= ActivityTypes.PLAYING;
49
57
  if (typeof activity.type === 'string') activity.type = ActivityTypes[activity.type];
50
58
 
51
- if (CustomStatusActivityTypes.includes(activity.type) && !activity.state) {
52
- activity.state = activity.name;
53
- activity.name = 'Custom Status';
59
+ if (CustomStatusActivityTypes.includes(activity.type)) {
60
+ hasCustom = true;
61
+ if (!activity.state) {
62
+ activity.state = activity.name;
63
+ activity.name = 'Custom Status';
64
+ }
54
65
  }
55
66
 
56
67
  data.activities.push(activity);
57
68
  }
69
+
70
+ // Si le tableau d'activités passé ne contient pas de custom status,
71
+ // réinjecte celui du cache pour ne pas le perdre
72
+ if (!hasCustom && existingCustom) {
73
+ data.activities.unshift({
74
+ type: typeof existingCustom.type === 'string'
75
+ ? ActivityTypes[existingCustom.type]
76
+ : existingCustom.type,
77
+ name: existingCustom.name ?? 'Custom Status',
78
+ state: existingCustom.state ?? null,
79
+ emoji: existingCustom.emoji ?? null,
80
+ });
81
+ }
58
82
  } else if (!activities && (status || afk || since) && this.activities.length) {
59
83
  data.activities.push(
60
84
  ...this.activities.map(a => {
@@ -74,4 +98,4 @@ module.exports = ClientPresence;
74
98
  /**
75
99
  * @external APIPresence
76
100
  * @see {@link https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields}
77
- */
101
+ */
@@ -267,6 +267,68 @@ class ClientUser extends User {
267
267
  return this.setPresence({ afk, shardId });
268
268
  }
269
269
 
270
+ /**
271
+ * Sets the custom status of the client user.
272
+ * Updates both the Gateway (local real-time display) and the REST API (account persistence).
273
+ * @param {CustomStatus|CustomStatusOptions|string|null} options
274
+ * A `CustomStatus` instance, an options object, a plain string for the state, or `null` to clear.
275
+ * @param {string} [options.state] Text displayed as the status
276
+ * @param {EmojiIdentifierResolvable} [options.emoji] Emoji shown next to the text
277
+ * @param {string} [options.expiresAt] ISO8601 date at which Discord auto-clears the status
278
+ * @param {number|number[]} [options.shardId] Shard(s) to broadcast the Gateway update on
279
+ * @returns {Promise<ClientPresence>}
280
+ * @example
281
+ * // With text + unicode emoji
282
+ * await client.user.setCustomStatus({ state: 'Coding...', emoji: '💻' });
283
+ * @example
284
+ * // Plain string shorthand
285
+ * await client.user.setCustomStatus('Taking a break');
286
+ * @example
287
+ * // Clear the custom status
288
+ * await client.user.setCustomStatus(null);
289
+ */
290
+ async setCustomStatus(options, shardId) {
291
+ const { CustomStatus } = require('../structures/Presence');
292
+
293
+ // ── 1. Normalise l'entrée ─────────────────────────────────────────────
294
+ let activity = null; // null = effacer
295
+ let restPayload = null;
296
+
297
+ if (options !== null && options !== undefined) {
298
+ if (typeof options === 'string') options = { state: options };
299
+
300
+ // Supporte un objet CustomStatus existant (déjà construit par l'utilisateur)
301
+ const cs = options instanceof CustomStatus
302
+ ? options
303
+ : new CustomStatus(this.client, {
304
+ state: options.state ?? null,
305
+ emoji: options.emoji ? { name: options.emoji } : undefined,
306
+ });
307
+
308
+ activity = cs.toJSON(); // { name, type, state, emoji }
309
+
310
+ restPayload = {
311
+ text: cs.state ?? null,
312
+ emoji_name: cs.emoji?.name ?? null,
313
+ emoji_id: cs.emoji?.id ?? null,
314
+ expires_at: options.expiresAt ?? null,
315
+ };
316
+ }
317
+
318
+ // ── 2. Gateway — visible localement en temps réel ────────────────────
319
+ const presenceActivities = activity ? [activity] : [];
320
+ const presence = this.setPresence({ activities: presenceActivities, shardId });
321
+
322
+ // ── 3. REST — persistance sur le compte Discord ───────────────────────
323
+ await this.client.api.users('@me').settings.patch({ data: { custom_status: restPayload } })
324
+ .catch(err => {
325
+ // Non bloquant : on ne veut pas casser setPresence si l'endpoint échoue
326
+ this.client.emit('warn', `[setCustomStatus] REST patch failed: ${err?.message ?? err}`);
327
+ });
328
+
329
+ return presence;
330
+ }
331
+
270
332
  /**
271
333
  * Sets the banner of the logged in client.
272
334
  * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
@@ -466,10 +528,10 @@ class ClientUser extends User {
466
528
 
467
529
  // Get current widgets first
468
530
  const currentWidgets = await this.widgetsList();
469
-
531
+
470
532
  // Find existing widget of this type or create new one
471
533
  let targetWidget = currentWidgets.widgets.find(w => w.data.type === type);
472
-
534
+
473
535
  if (!targetWidget) {
474
536
  // Create new widget if it doesn't exist
475
537
  targetWidget = {
@@ -488,7 +550,7 @@ class ClientUser extends User {
488
550
  const gameData = { game_id: gameId };
489
551
  if (comment !== null) gameData.comment = comment;
490
552
  if (tags.length > 0) gameData.tags = tags;
491
-
553
+
492
554
  targetWidget.data.games.push(gameData);
493
555
  }
494
556
 
@@ -516,7 +578,7 @@ class ClientUser extends User {
516
578
 
517
579
  // Get current widgets
518
580
  const currentWidgets = await this.widgetsList();
519
-
581
+
520
582
  if (gameId) {
521
583
  // Remove specific game from widget
522
584
  const targetWidget = currentWidgets.widgets.find(w => w.data.type === type);
@@ -611,7 +673,7 @@ class ClientUser extends User {
611
673
 
612
674
  const primaryColor = resolveColor(color1);
613
675
  const colors = [primaryColor];
614
-
676
+
615
677
  if (color2 !== null) {
616
678
  const secondaryColor = resolveColor(color2);
617
679
  colors.push(secondaryColor);
@@ -629,6 +691,44 @@ class ClientUser extends User {
629
691
  return this;
630
692
  }
631
693
 
694
+ /**
695
+ * Search messages across all DMs/GDMs using the tabs API.
696
+ * Supports pagination via offset.
697
+ * @param {Object} [options] Search options
698
+ * @param {string} [options.sortBy='timestamp'] Sort by ('timestamp' or 'relevance')
699
+ * @param {string} [options.sortOrder='desc'] Sort order ('asc' or 'desc')
700
+ * @param {number} [options.offset=0] Pagination offset
701
+ * @param {number} [options.limit=25] Results per page (max 25)
702
+ * @param {boolean} [options.trackExactTotal=true] Track exact total hits
703
+ * @returns {Promise<{ messages: Message[], total_results: number }>}
704
+ */
705
+ async searchTab(options = {}) {
706
+ const {
707
+ sortBy = 'timestamp',
708
+ sortOrder = 'desc',
709
+ offset = 0,
710
+ limit = 25,
711
+ trackExactTotal = true,
712
+ } = options;
713
+
714
+ const data = await this.client.api.users['@me'].messages.search.tabs.post({
715
+ data: {
716
+ tabs: {
717
+ messages: {
718
+ sort_by: sortBy,
719
+ sort_order: sortOrder,
720
+ author_id: [this.id],
721
+ offset,
722
+ limit: Math.min(limit, 25),
723
+ },
724
+ },
725
+ track_exact_total_hits: trackExactTotal,
726
+ },
727
+ });
728
+
729
+ return data;
730
+ }
731
+
632
732
  /**
633
733
  * Set the TAG of a guild.
634
734
  * @param {GuildIDResolve} guild The guild with the tag
@@ -637,7 +737,7 @@ class ClientUser extends User {
637
737
  setClan(guild) {
638
738
  const id = this.client.guilds.resolveId(guild);
639
739
  if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
640
-
740
+
641
741
  return this.client.api.users['@me'].clan.put({ data: { identity_guild_id: id, identity_enabled: true } });
642
742
  }
643
743
 
@@ -650,4 +750,4 @@ class ClientUser extends User {
650
750
  }
651
751
  }
652
752
 
653
- module.exports = ClientUser;
753
+ module.exports = ClientUser;
@@ -1034,6 +1034,7 @@ export class ClientUser extends User {
1034
1034
  public delWidget(type: WidgetType, gameId?: string): Promise<any>;
1035
1035
  public widgetsList(): Promise<WidgetsResponse>;
1036
1036
  public setNameStyle(fontName: FontName | number, effectName: EffectName | number, color1: number | string, color2?: number | string | null): Promise<this>;
1037
+ public searchTab(options?: UserMessageSearchTabOptions): Promise<{ messages: any[][]; total_results: number }>;
1037
1038
  }
1038
1039
 
1039
1040
  export class Options extends null {
@@ -1723,6 +1724,14 @@ export class Guild extends AnonymousGuild {
1723
1724
  public search(options?: GuildSearchOptions): Promise<any>;
1724
1725
  }
1725
1726
 
1727
+ export interface UserMessageSearchTabOptions {
1728
+ sortBy?: 'timestamp' | 'relevance';
1729
+ sortOrder?: 'desc' | 'asc';
1730
+ offset?: number;
1731
+ limit?: number;
1732
+ trackExactTotal?: boolean;
1733
+ }
1734
+
1726
1735
  export class GuildAuditLogs<T extends GuildAuditLogsResolvable = 'ALL'> {
1727
1736
  private constructor(guild: Guild, data: RawGuildAuditLogData);
1728
1737
  private webhooks: Collection<Snowflake, Webhook>;