From 32d614ccd389622e2969d59582f80d07a35eb39c Mon Sep 17 00:00:00 2001
From: Jiralite <33201955+Jiralite@users.noreply.github.com>
Date: Mon, 18 Sep 2023 08:26:22 +0100
Subject: [PATCH] fix(GuildScheduledEvent): Use `if...else` pattern and handle
 partials (#9802)

fix: handle partial case

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
 .../src/structures/GuildScheduledEvent.js     | 109 +++++++++++++-----
 packages/discord.js/typings/index.d.ts        |  22 +++-
 2 files changed, 94 insertions(+), 37 deletions(-)

diff --git a/packages/discord.js/src/structures/GuildScheduledEvent.js b/packages/discord.js/src/structures/GuildScheduledEvent.js
index e9a37b2b0b86..66f189eeedf1 100644
--- a/packages/discord.js/src/structures/GuildScheduledEvent.js
+++ b/packages/discord.js/src/structures/GuildScheduledEvent.js
@@ -50,11 +50,16 @@ class GuildScheduledEvent extends Base {
       this.creatorId ??= null;
     }
 
-    /**
-     * The name of the guild scheduled event
-     * @type {string}
-     */
-    this.name = data.name;
+    if ('name' in data) {
+      /**
+       * The name of the guild scheduled event
+       * @type {?string}
+       */
+      this.name = data.name;
+    } else {
+      // Only if partial.
+      this.name ??= null;
+    }
 
     if ('description' in data) {
       /**
@@ -66,37 +71,59 @@ class GuildScheduledEvent extends Base {
       this.description ??= null;
     }
 
-    /**
-     * The timestamp the guild scheduled event will start at
-     * <info>This can be potentially `null` only when it's an {@link AuditLogEntryTarget}</info>
-     * @type {?number}
-     */
-    this.scheduledStartTimestamp = data.scheduled_start_time ? Date.parse(data.scheduled_start_time) : null;
+    if ('scheduled_start_time' in data) {
+      /**
+       * The timestamp the guild scheduled event will start at
+       * @type {?number}
+       */
+      this.scheduledStartTimestamp = Date.parse(data.scheduled_start_time);
+    } else {
+      this.scheduledStartTimestamp ??= null;
+    }
 
-    /**
-     * The timestamp the guild scheduled event will end at,
-     * or `null` if the event does not have a scheduled time to end
-     * @type {?number}
-     */
-    this.scheduledEndTimestamp = data.scheduled_end_time ? Date.parse(data.scheduled_end_time) : null;
+    if ('scheduled_end_time' in data) {
+      /**
+       * The timestamp the guild scheduled event will end at
+       * or `null` if the event does not have a scheduled time to end
+       * @type {?number}
+       */
+      this.scheduledEndTimestamp = data.scheduled_end_time ? Date.parse(data.scheduled_end_time) : null;
+    } else {
+      this.scheduledEndTimestamp ??= null;
+    }
 
-    /**
-     * The privacy level of the guild scheduled event
-     * @type {GuildScheduledEventPrivacyLevel}
-     */
-    this.privacyLevel = data.privacy_level;
+    if ('privacy_level' in data) {
+      /**
+       * The privacy level of the guild scheduled event
+       * @type {?GuildScheduledEventPrivacyLevel}
+       */
+      this.privacyLevel = data.privacy_level;
+    } else {
+      // Only if partial.
+      this.privacyLevel ??= null;
+    }
 
-    /**
-     * The status of the guild scheduled event
-     * @type {GuildScheduledEventStatus}
-     */
-    this.status = data.status;
+    if ('status' in data) {
+      /**
+       * The status of the guild scheduled event
+       * @type {?GuildScheduledEventStatus}
+       */
+      this.status = data.status;
+    } else {
+      // Only if partial.
+      this.status ??= null;
+    }
 
-    /**
-     * The type of hosting entity associated with the scheduled event
-     * @type {GuildScheduledEventEntityType}
-     */
-    this.entityType = data.entity_type;
+    if ('entity_type' in data) {
+      /**
+       * The type of hosting entity associated with the scheduled event
+       * @type {?GuildScheduledEventEntityType}
+       */
+      this.entityType = data.entity_type;
+    } else {
+      // Only if partial.
+      this.entityType ??= null;
+    }
 
     if ('entity_id' in data) {
       /**
@@ -164,6 +191,15 @@ class GuildScheduledEvent extends Base {
     }
   }
 
+  /**
+   * Whether this guild scheduled event is partial.
+   * @type {boolean}
+   * @readonly
+   */
+  get partial() {
+    return this.name === null;
+  }
+
   /**
    * The URL of this scheduled event's cover image
    * @param {BaseImageURLOptions} [options={}] Options for image URL
@@ -276,6 +312,15 @@ class GuildScheduledEvent extends Base {
     return this.guild.scheduledEvents.edit(this.id, options);
   }
 
+  /**
+   * Fetches this guild scheduled event.
+   * @param {boolean} [force=true] Whether to skip the cache check and request the API
+   * @returns {Promise<GuildScheduledEvent>}
+   */
+  fetch(force = true) {
+    return this.guild.scheduledEvents.fetch({ guildScheduledEvent: this.id, force });
+  }
+
   /**
    * Deletes this guild scheduled event.
    * @returns {Promise<GuildScheduledEvent>}
diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts
index e5c85cf45749..fd39f9a97c15 100644
--- a/packages/discord.js/typings/index.d.ts
+++ b/packages/discord.js/typings/index.d.ts
@@ -1670,11 +1670,13 @@ export class GuildScheduledEvent<S extends GuildScheduledEventStatus = GuildSche
   public get guild(): Guild | null;
   public get url(): string;
   public image: string | null;
+  public get partial(): false;
   public coverImageURL(options?: Readonly<BaseImageURLOptions>): string | null;
   public createInviteURL(options?: GuildScheduledEventInviteURLCreateOptions): Promise<string>;
   public edit<T extends GuildScheduledEventSetStatusArg<S>>(
     options: GuildScheduledEventEditOptions<S, T>,
   ): Promise<GuildScheduledEvent<T>>;
+  public fetch(force?: boolean): Promise<GuildScheduledEvent<S>>;
   public delete(): Promise<GuildScheduledEvent<S>>;
   public setName(name: string, reason?: string): Promise<GuildScheduledEvent<S>>;
   public setScheduledStartTime(scheduledStartTime: DateResolvable, reason?: string): Promise<GuildScheduledEvent<S>>;
@@ -4359,7 +4361,14 @@ export interface AddGuildMemberOptions {
   fetchWhenExisting?: boolean;
 }
 
-export type AllowedPartial = User | Channel | GuildMember | Message | MessageReaction | ThreadMember;
+export type AllowedPartial =
+  | User
+  | Channel
+  | GuildMember
+  | Message
+  | MessageReaction
+  | GuildScheduledEvent
+  | ThreadMember;
 
 export type AllowedThreadTypeForNewsChannel = ChannelType.AnnouncementThread;
 
@@ -4929,12 +4938,12 @@ export interface ClientEvents {
   stickerUpdate: [oldSticker: Sticker, newSticker: Sticker];
   guildScheduledEventCreate: [guildScheduledEvent: GuildScheduledEvent];
   guildScheduledEventUpdate: [
-    oldGuildScheduledEvent: GuildScheduledEvent | null,
+    oldGuildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent | null,
     newGuildScheduledEvent: GuildScheduledEvent,
   ];
-  guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent];
-  guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent, user: User];
-  guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent, user: User];
+  guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent];
+  guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent, user: User];
+  guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent, user: User];
 }
 
 export interface ClientFetchInviteOptions {
@@ -6149,6 +6158,9 @@ export interface PartialMessage
 
 export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
 
+export interface PartialGuildScheduledEvent
+  extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
+
 export interface PartialThreadMember extends Partialize<ThreadMember, 'flags' | 'joinedAt' | 'joinedTimestamp'> {}
 
 export interface PartialOverwriteData {