From 43a995bef0dbe7c18631a20816ef6438f5c2cc51 Mon Sep 17 00:00:00 2001
From: Jiralite <33201955+Jiralite@users.noreply.github.com>
Date: Thu, 17 Jul 2025 13:34:25 +0100
Subject: [PATCH] refactor!: Invite classes (#10888)
BREAKING CHANGE: Invites are now split up into classes based on their type.
---
packages/discord.js/src/client/Client.js | 13 +-
.../websocket/handlers/INVITE_CREATE.js | 2 +-
.../websocket/handlers/INVITE_DELETE.js | 6 +-
packages/discord.js/src/index.js | 4 +-
.../src/managers/GuildInviteManager.js | 38 +-
.../discord.js/src/managers/GuildManager.js | 6 +-
.../discord.js/src/structures/BaseInvite.js | 187 ++++++++++
.../src/structures/GroupDMInvite.js | 37 ++
.../src/structures/GuildAuditLogsEntry.js | 4 +-
.../discord.js/src/structures/GuildInvite.js | 211 +++++++++++
packages/discord.js/src/structures/Invite.js | 345 ------------------
packages/discord.js/src/util/DataResolver.js | 4 +-
packages/discord.js/src/util/Invites.js | 31 ++
packages/discord.js/typings/index.d.ts | 116 +++---
packages/discord.js/typings/index.test-d.ts | 17 +-
15 files changed, 595 insertions(+), 426 deletions(-)
create mode 100644 packages/discord.js/src/structures/BaseInvite.js
create mode 100644 packages/discord.js/src/structures/GroupDMInvite.js
create mode 100644 packages/discord.js/src/structures/GuildInvite.js
delete mode 100644 packages/discord.js/src/structures/Invite.js
create mode 100644 packages/discord.js/src/util/Invites.js
diff --git a/packages/discord.js/src/client/Client.js b/packages/discord.js/src/client/Client.js
index 7add1a75d..dcedc0f76 100644
--- a/packages/discord.js/src/client/Client.js
+++ b/packages/discord.js/src/client/Client.js
@@ -14,7 +14,6 @@ const { ShardClientUtil } = require('../sharding/ShardClientUtil.js');
const { ClientPresence } = require('../structures/ClientPresence.js');
const { GuildPreview } = require('../structures/GuildPreview.js');
const { GuildTemplate } = require('../structures/GuildTemplate.js');
-const { Invite } = require('../structures/Invite.js');
const { SoundboardSound } = require('../structures/SoundboardSound.js');
const { Sticker } = require('../structures/Sticker.js');
const { StickerPack } = require('../structures/StickerPack.js');
@@ -24,6 +23,7 @@ const { Widget } = require('../structures/Widget.js');
const { resolveInviteCode, resolveGuildTemplateCode } = require('../util/DataResolver.js');
const { Events } = require('../util/Events.js');
const { IntentsBitField } = require('../util/IntentsBitField.js');
+const { createInvite } = require('../util/Invites.js');
const { Options } = require('../util/Options.js');
const { PermissionsBitField } = require('../util/PermissionsBitField.js');
const { Status } = require('../util/Status.js');
@@ -459,6 +459,7 @@ class Client extends BaseClient {
* Options used when fetching an invite from Discord.
*
* @typedef {Object} ClientFetchInviteOptions
+ * @property {boolean} [withCounts] Whether to include approximate member counts
* @property {Snowflake} [guildScheduledEventId] The id of the guild scheduled event to include with
* the invite
*/
@@ -474,14 +475,16 @@ class Client extends BaseClient {
* .then(invite => console.log(`Obtained invite with code: ${invite.code}`))
* .catch(console.error);
*/
- async fetchInvite(invite, options) {
+ async fetchInvite(invite, { withCounts, guildScheduledEventId } = {}) {
const code = resolveInviteCode(invite);
+
const query = makeURLSearchParams({
- with_counts: true,
- guild_scheduled_event_id: options?.guildScheduledEventId,
+ with_counts: withCounts,
+ guild_scheduled_event_id: guildScheduledEventId,
});
+
const data = await this.rest.get(Routes.invite(code), { query });
- return new Invite(this, data);
+ return createInvite(this, data);
}
/**
diff --git a/packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js b/packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
index 16ab2ab41..d3e1fc7bb 100644
--- a/packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
+++ b/packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
@@ -15,7 +15,7 @@ module.exports = (client, { d: data }) => {
* This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.
*
* @event Client#inviteCreate
- * @param {Invite} invite The invite that was created
+ * @param {GuildInvite} invite The invite that was created
*/
client.emit(Events.InviteCreate, invite);
};
diff --git a/packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js b/packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
index 9ca09a5bb..aa124683f 100644
--- a/packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
+++ b/packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
@@ -1,6 +1,6 @@
'use strict';
-const { Invite } = require('../../../structures/Invite.js');
+const { GuildInvite } = require('../../../structures/GuildInvite.js');
const { Events } = require('../../../util/Events.js');
module.exports = (client, { d: data }) => {
@@ -9,7 +9,7 @@ module.exports = (client, { d: data }) => {
if (!channel) return;
const inviteData = Object.assign(data, { channel, guild });
- const invite = new Invite(client, inviteData);
+ const invite = new GuildInvite(client, inviteData);
guild.invites.cache.delete(invite.code);
@@ -18,7 +18,7 @@ module.exports = (client, { d: data }) => {
* This event requires the {@link PermissionFlagsBits.ManageChannels} permission for the channel.
*
* @event Client#inviteDelete
- * @param {Invite} invite The invite that was deleted
+ * @param {GuildInvite} invite The invite that was deleted
*/
client.emit(Events.InviteDelete, invite);
};
diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js
index f382df4e4..53af1b0fd 100644
--- a/packages/discord.js/src/index.js
+++ b/packages/discord.js/src/index.js
@@ -124,6 +124,7 @@ exports.BaseGuildEmoji = require('./structures/BaseGuildEmoji.js').BaseGuildEmoj
exports.BaseGuildTextChannel = require('./structures/BaseGuildTextChannel.js').BaseGuildTextChannel;
exports.BaseGuildVoiceChannel = require('./structures/BaseGuildVoiceChannel.js').BaseGuildVoiceChannel;
exports.BaseInteraction = require('./structures/BaseInteraction.js').BaseInteraction;
+exports.BaseInvite = require('./structures/BaseInvite.js').BaseInvite;
exports.BaseSelectMenuComponent = require('./structures/BaseSelectMenuComponent.js').BaseSelectMenuComponent;
exports.ButtonComponent = require('./structures/ButtonComponent.js').ButtonComponent;
exports.ButtonInteraction = require('./structures/ButtonInteraction.js').ButtonInteraction;
@@ -151,12 +152,14 @@ exports.Emoji = require('./structures/Emoji.js').Emoji;
exports.Entitlement = require('./structures/Entitlement.js').Entitlement;
exports.FileComponent = require('./structures/FileComponent.js').FileComponent;
exports.ForumChannel = require('./structures/ForumChannel.js').ForumChannel;
+exports.GroupDMInvite = require('./structures/GroupDMInvite.js').GroupDMInvite;
exports.Guild = require('./structures/Guild.js').Guild;
exports.GuildAuditLogs = require('./structures/GuildAuditLogs.js').GuildAuditLogs;
exports.GuildAuditLogsEntry = require('./structures/GuildAuditLogsEntry.js').GuildAuditLogsEntry;
exports.GuildBan = require('./structures/GuildBan.js').GuildBan;
exports.GuildChannel = require('./structures/GuildChannel.js').GuildChannel;
exports.GuildEmoji = require('./structures/GuildEmoji.js').GuildEmoji;
+exports.GuildInvite = require('./structures/GuildInvite.js').GuildInvite;
exports.GuildMember = require('./structures/GuildMember.js').GuildMember;
exports.GuildOnboarding = require('./structures/GuildOnboarding.js').GuildOnboarding;
exports.GuildOnboardingPrompt = require('./structures/GuildOnboardingPrompt.js').GuildOnboardingPrompt;
@@ -175,7 +178,6 @@ exports.InteractionCallbackResponse =
require('./structures/InteractionCallbackResponse.js').InteractionCallbackResponse;
exports.InteractionCollector = require('./structures/InteractionCollector.js').InteractionCollector;
exports.InteractionWebhook = require('./structures/InteractionWebhook.js').InteractionWebhook;
-exports.Invite = require('./structures/Invite.js').Invite;
exports.InviteGuild = require('./structures/InviteGuild.js').InviteGuild;
exports.MediaChannel = require('./structures/MediaChannel.js').MediaChannel;
exports.MediaGalleryComponent = require('./structures/MediaGalleryComponent.js').MediaGalleryComponent;
diff --git a/packages/discord.js/src/managers/GuildInviteManager.js b/packages/discord.js/src/managers/GuildInviteManager.js
index 69b8b0458..960dbf364 100644
--- a/packages/discord.js/src/managers/GuildInviteManager.js
+++ b/packages/discord.js/src/managers/GuildInviteManager.js
@@ -3,7 +3,7 @@
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v10');
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
-const { Invite } = require('../structures/Invite.js');
+const { GuildInvite } = require('../structures/GuildInvite.js');
const { resolveInviteCode } = require('../util/DataResolver.js');
const { CachedManager } = require('./CachedManager.js');
@@ -14,7 +14,7 @@ const { CachedManager } = require('./CachedManager.js');
*/
class GuildInviteManager extends CachedManager {
constructor(guild, iterable) {
- super(guild.client, Invite, iterable);
+ super(guild.client, GuildInvite, iterable);
/**
* The guild this Manager belongs to
@@ -27,7 +27,7 @@ class GuildInviteManager extends CachedManager {
/**
* The cache of this Manager
*
- * @type {Collection}
+ * @type {Collection}
* @name GuildInviteManager#cache
*/
@@ -36,35 +36,44 @@ class GuildInviteManager extends CachedManager {
}
/**
- * Data that resolves to give an Invite object. This can be:
+ * Data that resolves to give a `GuildInvite`. This can be:
+ *
* - An invite code
* - An invite URL
*
- * @typedef {string} InviteResolvable
+ * @typedef {string} GuildInviteResolvable
*/
/**
- * Data that can be resolved to a channel that an invite can be created on. This can be:
+ * A guild channel where an invite may be created on. This can be:
* - TextChannel
* - VoiceChannel
* - AnnouncementChannel
* - StageChannel
* - ForumChannel
* - MediaChannel
+ *
+ * @typedef {TextChannel|VoiceChannel|AnnouncementChannel|StageChannel|ForumChannel|MediaChannel}
+ * GuildInvitableChannel
+ */
+
+ /**
+ * Data that can be resolved to a guild channel where an invite may be created on. This can be:
+ * - GuildInvitableChannel
* - Snowflake
*
- * @typedef {TextChannel|VoiceChannel|AnnouncementChannel|StageChannel|ForumChannel|MediaChannel|Snowflake}
+ * @typedef {GuildInvitableChannel|Snowflake}
* GuildInvitableChannelResolvable
*/
/**
- * Resolves an InviteResolvable to an Invite object.
+ * Resolves an `GuildInviteResolvable` to a `GuildInvite` object.
*
* @method resolve
* @memberof GuildInviteManager
* @instance
- * @param {InviteResolvable} invite The invite resolvable to resolve
- * @returns {?Invite}
+ * @param {GuildInviteResolvable} invite The invite resolvable to resolve
+ * @returns {?GuildInvite}
*/
/**
@@ -98,8 +107,9 @@ class GuildInviteManager extends CachedManager {
/**
* Fetches invite(s) from Discord.
*
- * @param {InviteResolvable|FetchInviteOptions|FetchInvitesOptions} [options] Options for fetching guild invite(s)
- * @returns {Promise>}
+ * @param {GuildInviteResolvable|FetchInviteOptions|FetchInvitesOptions} [options]
+ * Options for fetching guild invite(s)
+ * @returns {Promise>}
* @example
* // Fetch all invites from a guild
* guild.invites.fetch()
@@ -183,7 +193,7 @@ class GuildInviteManager extends CachedManager {
*
* @param {GuildInvitableChannelResolvable} channel The options for creating the invite from a channel.
* @param {InviteCreateOptions} [options={}] The options for creating the invite from a channel.
- * @returns {Promise}
+ * @returns {Promise}
* @example
* // Create an invite to a selected channel
* guild.invites.create('599942732013764608')
@@ -209,7 +219,7 @@ class GuildInviteManager extends CachedManager {
},
reason,
});
- return new Invite(this.client, invite);
+ return new GuildInvite(this.client, invite);
}
/**
diff --git a/packages/discord.js/src/managers/GuildManager.js b/packages/discord.js/src/managers/GuildManager.js
index 3f56ffa0a..7453a044c 100644
--- a/packages/discord.js/src/managers/GuildManager.js
+++ b/packages/discord.js/src/managers/GuildManager.js
@@ -10,8 +10,8 @@ const { ShardClientUtil } = require('../sharding/ShardClientUtil.js');
const { Guild } = require('../structures/Guild.js');
const { GuildChannel } = require('../structures/GuildChannel.js');
const { GuildEmoji } = require('../structures/GuildEmoji.js');
+const { GuildInvite } = require('../structures/GuildInvite.js');
const { GuildMember } = require('../structures/GuildMember.js');
-const { Invite } = require('../structures/Invite.js');
const { OAuth2Guild } = require('../structures/OAuth2Guild.js');
const { Role } = require('../structures/Role.js');
const { resolveImage } = require('../util/DataResolver.js');
@@ -119,7 +119,7 @@ class GuildManager extends CachedManager {
guild instanceof GuildMember ||
guild instanceof GuildEmoji ||
guild instanceof Role ||
- (guild instanceof Invite && guild.guild)
+ (guild instanceof GuildInvite && guild.guild)
) {
return super.resolve(guild.guild);
}
@@ -142,7 +142,7 @@ class GuildManager extends CachedManager {
guild instanceof GuildMember ||
guild instanceof GuildEmoji ||
guild instanceof Role ||
- (guild instanceof Invite && guild.guild)
+ (guild instanceof GuildInvite && guild.guild)
) {
return super.resolveId(guild.guild.id);
}
diff --git a/packages/discord.js/src/structures/BaseInvite.js b/packages/discord.js/src/structures/BaseInvite.js
new file mode 100644
index 000000000..024b08fa8
--- /dev/null
+++ b/packages/discord.js/src/structures/BaseInvite.js
@@ -0,0 +1,187 @@
+'use strict';
+
+const { RouteBases } = require('discord-api-types/v10');
+const { Base } = require('./Base.js');
+
+/**
+ * The base invite class.
+ *
+ * @extends {Base}
+ */
+class BaseInvite extends Base {
+ constructor(client, data) {
+ super(client);
+
+ /**
+ * The type of this invite.
+ *
+ * @type {InviteType}
+ */
+ this.type = data.type;
+
+ /**
+ * The invite code.
+ *
+ * @type {string}
+ */
+ this.code = data.code;
+
+ this._patch(data);
+ }
+
+ _patch(data) {
+ if ('inviter_id' in data) {
+ /**
+ * The id of the user that created this invite.
+ *
+ * @type {?Snowflake}
+ */
+ this.inviterId = data.inviter_id;
+ } else {
+ this.inviterId ??= null;
+ }
+
+ if ('inviter' in data) {
+ this.client.users._add(data.inviter);
+ this.inviterId ??= data.inviter.id;
+ }
+
+ if ('max_age' in data) {
+ /**
+ * The maximum age of the invite in seconds. `0` for no expiry.
+ *
+ * @type {?number}
+ */
+ this.maxAge = data.max_age;
+ } else {
+ this.maxAge ??= null;
+ }
+
+ if ('created_at' in data) {
+ /**
+ * The timestamp this invite was created at.
+ *
+ * @type {?number}
+ */
+ this.createdTimestamp = Date.parse(data.created_at);
+ } else {
+ this.createdTimestamp ??= null;
+ }
+
+ if ('expires_at' in data) {
+ this._expiresTimestamp = data.expires_at && Date.parse(data.expires_at);
+ } else {
+ this._expiresTimestamp ??= null;
+ }
+
+ if ('channel_id' in data) {
+ /**
+ * The id of the channel this invite is for.
+ *
+ * @type {?Snowflake}
+ */
+ this.channelId = data.channel_id;
+ }
+
+ if ('approximate_member_count' in data) {
+ /**
+ * The approximate total number of members.
+ *
+ * @type {?number}
+ */
+ this.approximateMemberCount = data.approximate_member_count;
+ } else {
+ this.approximateMemberCount ??= null;
+ }
+ }
+
+ /**
+ * The user that created this invite.
+ *
+ * @type {?User}
+ * @readonly
+ */
+ get inviter() {
+ return this.inviterId && this.client.users.resolve(this.inviterId);
+ }
+
+ /**
+ * The creation date of this invite.
+ *
+ * @type {?Date}
+ * @readonly
+ */
+ get createdAt() {
+ return this.createdTimestamp && new Date(this.createdTimestamp);
+ }
+
+ /**
+ * The timestamp this invite expires at.
+ *
+ * @type {?number}
+ * @readonly
+ */
+ get expiresTimestamp() {
+ return (
+ this._expiresTimestamp ??
+ (this.createdTimestamp && this.maxAge ? this.createdTimestamp + this.maxAge * 1_000 : null)
+ );
+ }
+
+ /**
+ * The expiry date of this invite.
+ *
+ * @type {?Date}
+ * @readonly
+ */
+ get expiresAt() {
+ return this.expiresTimestamp && new Date(this.expiresTimestamp);
+ }
+
+ /**
+ * The URL to the invite.
+ *
+ * @type {string}
+ * @readonly
+ */
+ get url() {
+ return `${RouteBases.invite}/${this.code}`;
+ }
+
+ /**
+ * A regular expression that matches Discord invite links.
+ * The `code` group property is present on the `exec()` result of this expression.
+ *
+ * @type {RegExp}
+ * @memberof BaseInvite
+ */
+ static InvitesPattern = /discord(?:(?:app)?\.com\/invite|\.gg(?:\/invite)?)\/(?[\w-]{2,255})/i;
+
+ /**
+ * When concatenated with a string, this automatically concatenates the invite's URL instead of the object.
+ *
+ * @returns {string}
+ * @example
+ * // Logs: Invite: https://discord.gg/djs
+ * console.log(`Invite: ${invite}`);
+ */
+ toString() {
+ return this.url;
+ }
+
+ toJSON() {
+ return super.toJSON({
+ url: true,
+ expiresTimestamp: true,
+ uses: false,
+ channel: 'channelId',
+ inviter: 'inviterId',
+ });
+ }
+
+ valueOf() {
+ return this.code;
+ }
+}
+
+exports.BaseInvite = BaseInvite;
diff --git a/packages/discord.js/src/structures/GroupDMInvite.js b/packages/discord.js/src/structures/GroupDMInvite.js
new file mode 100644
index 000000000..e5a41cc8b
--- /dev/null
+++ b/packages/discord.js/src/structures/GroupDMInvite.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const { BaseInvite } = require('./BaseInvite.js');
+
+/**
+ * A channel invite leading to a group direct message channel.
+ *
+ * @extends {BaseInvite}
+ */
+class GroupDMInvite extends BaseInvite {
+ /**
+ * The approximate total number of members of in the group direct message channel.
+ * This is only available when the invite was fetched through {@link Client#fetchInvite}.
+ *
+ * @name GroupDMInvite#approximateMemberCount
+ * @type {?number}
+ */
+
+ _patch(data) {
+ super._patch(data);
+
+ if ('channel' in data) {
+ /**
+ * The channel this invite is for.
+ *
+ * @type {?PartialGroupDMChannel}
+ */
+ this.channel =
+ this.client.channels._add(data.channel, null, { cache: false }) ??
+ this.client.channels.cache.get(this.channelId);
+
+ this.channelId ??= data.channel.id;
+ }
+ }
+}
+
+exports.GroupDMInvite = GroupDMInvite;
diff --git a/packages/discord.js/src/structures/GuildAuditLogsEntry.js b/packages/discord.js/src/structures/GuildAuditLogsEntry.js
index 697b7c67f..dd4ab4e5e 100644
--- a/packages/discord.js/src/structures/GuildAuditLogsEntry.js
+++ b/packages/discord.js/src/structures/GuildAuditLogsEntry.js
@@ -5,10 +5,10 @@ const { AuditLogOptionsType, AuditLogEvent } = require('discord-api-types/v10');
const { Partials } = require('../util/Partials.js');
const { flatten } = require('../util/Util.js');
const { AutoModerationRule } = require('./AutoModerationRule.js');
+const { GuildInvite } = require('./GuildInvite.js');
const { GuildOnboardingPrompt } = require('./GuildOnboardingPrompt.js');
const { GuildScheduledEvent } = require('./GuildScheduledEvent.js');
const { Integration } = require('./Integration.js');
-const { Invite } = require('./Invite.js');
const { StageInstance } = require('./StageInstance.js');
const { Sticker } = require('./Sticker.js');
const { Webhook } = require('./Webhook.js');
@@ -337,7 +337,7 @@ class GuildAuditLogsEntry {
this.target =
guild.invites.cache.get(inviteChange.new ?? inviteChange.old) ??
- new Invite(guild.client, changesReduce(this.changes, { guild }));
+ new GuildInvite(guild.client, changesReduce(this.changes, { guild }));
} else if (targetType === Targets.Message) {
// Discord sends a channel id for the MessageBulkDelete action type.
this.target =
diff --git a/packages/discord.js/src/structures/GuildInvite.js b/packages/discord.js/src/structures/GuildInvite.js
new file mode 100644
index 000000000..33e94a42e
--- /dev/null
+++ b/packages/discord.js/src/structures/GuildInvite.js
@@ -0,0 +1,211 @@
+'use strict';
+
+const { Routes, PermissionFlagsBits, InviteType } = require('discord-api-types/v10');
+const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
+const { BaseInvite } = require('./BaseInvite.js');
+const { GuildScheduledEvent } = require('./GuildScheduledEvent.js');
+const { IntegrationApplication } = require('./IntegrationApplication.js');
+const { InviteGuild } = require('./InviteGuild.js');
+
+/**
+ * A channel invite leading to a guild.
+ *
+ * @extends {BaseInvite}
+ */
+class GuildInvite extends BaseInvite {
+ constructor(client, data) {
+ super(client, data);
+
+ // Type may be missing from audit logs.
+ this.type = InviteType.Guild;
+
+ /**
+ * The id of the guild this invite is for.
+ *
+ * @type {Snowflake}
+ */
+ // Guild id may be missing from audit logs.
+ this.guildId = data.guild_id ?? data.guild.id;
+
+ /**
+ * The maximum age of the invite in seconds. `0` for no expiry.
+ * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
+ * or created through {@link GuildInviteManager#create}.
+ *
+ * @name GuildInvite#maxAge
+ * @type {?number}
+ */
+
+ /**
+ * The approximate total number of members of the guild.
+ * This is only available when the invite was fetched through {@link Client#fetchInvite}.
+ *
+ * @name GuildInvite#approximateMemberCount
+ * @type {?number}
+ */
+ }
+
+ _patch(data) {
+ super._patch(data);
+
+ if ('guild' in data) {
+ /**
+ * The guild the invite is for. May include welcome screen data.
+ *
+ * @type {?(Guild|InviteGuild)}
+ */
+ this.guild = this.client.guilds.cache.get(data.guild.id) ?? new InviteGuild(this.client, data.guild);
+ } else {
+ this.guild ??= null;
+ }
+
+ if ('channel' in data) {
+ /**
+ * The channel this invite is for.
+ *
+ * @type {?GuildInvitableChannel}
+ */
+ this.channel =
+ this.client.channels._add(data.channel, this.guild, { cache: false }) ??
+ this.client.channels.cache.get(this.channelId);
+
+ this.channelId ??= data.channel.id;
+ }
+
+ if ('target_type' in data) {
+ /**
+ * The target type.
+ *
+ * @type {?InviteTargetType}
+ */
+ this.targetType = data.target_type;
+ } else {
+ this.targetType ??= null;
+ }
+
+ if ('target_user' in data) {
+ /**
+ * The user whose stream to display for this voice channel stream invite.
+ *
+ * @type {?User}
+ */
+ this.targetUser = this.client.users._add(data.target_user);
+ } else {
+ this.targetUser ??= null;
+ }
+
+ if ('target_application' in data) {
+ /**
+ * The embedded application to open for this voice channel embedded application invite.
+ *
+ * @type {?IntegrationApplication}
+ */
+ this.targetApplication = new IntegrationApplication(this.client, data.target_application);
+ } else {
+ this.targetApplication ??= null;
+ }
+
+ if ('guild_scheduled_event' in data) {
+ /**
+ * The guild scheduled event data if there is a {@link GuildScheduledEvent} in the channel.
+ *
+ * @type {?GuildScheduledEvent}
+ */
+ this.guildScheduledEvent = new GuildScheduledEvent(this.client, data.guild_scheduled_event);
+ } else {
+ this.guildScheduledEvent ??= null;
+ }
+
+ if ('uses' in data) {
+ /**
+ * How many times this invite has been used.
+ * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
+ * or created through {@link GuildInviteManager#create}.
+ *
+ * @type {?number}
+ */
+ this.uses = data.uses;
+ } else {
+ this.uses ??= null;
+ }
+
+ if ('max_uses' in data) {
+ /**
+ * The maximum uses of this invite.
+ * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
+ * or created through {@link GuildInviteManager#create}.
+ *
+ * @type {?number}
+ */
+ this.maxUses = data.max_uses;
+ } else {
+ this.maxUses ??= null;
+ }
+
+ if ('temporary' in data) {
+ /**
+ * Whether this invite grants temporary membership.
+ * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
+ * or created through {@link GuildInviteManager#create}.
+ *
+ * @type {?boolean}
+ */
+ this.temporary = data.temporary ?? null;
+ } else {
+ this.temporary ??= null;
+ }
+
+ if ('approximate_presence_count' in data) {
+ /**
+ * The approximate number of online members of the guild.
+ * This is only available when the invite was fetched through {@link Client#fetchInvite}.
+ *
+ * @type {?number}
+ */
+ this.approximatePresenceCount = data.approximate_presence_count;
+ } else {
+ this.approximatePresenceCount ??= null;
+ }
+ }
+
+ /**
+ * Whether the invite is deletable by the client user.
+ *
+ * @type {boolean}
+ * @readonly
+ */
+ get deletable() {
+ const guild = this.guild;
+ if (!guild || !this.client.guilds.cache.has(guild.id)) return false;
+ if (!guild.members.me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
+ return Boolean(
+ this.channel?.permissionsFor(this.client.user).has(PermissionFlagsBits.ManageChannels, false) ||
+ guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild),
+ );
+ }
+
+ /**
+ * Delete this invite.
+ *
+ * @param {string} [reason] Reason for deleting this invite
+ * @returns {Promise}
+ */
+ async delete(reason) {
+ await this.client.rest.delete(Routes.invite(this.code), { reason });
+ }
+
+ toJSON() {
+ return super.toJSON({
+ url: true,
+ expiresTimestamp: true,
+ presenceCount: false,
+ memberCount: false,
+ uses: false,
+ channel: 'channelId',
+ inviter: 'inviterId',
+ guild: 'guildId',
+ });
+ }
+}
+
+exports.GuildInvite = GuildInvite;
diff --git a/packages/discord.js/src/structures/Invite.js b/packages/discord.js/src/structures/Invite.js
deleted file mode 100644
index c5b50ddee..000000000
--- a/packages/discord.js/src/structures/Invite.js
+++ /dev/null
@@ -1,345 +0,0 @@
-'use strict';
-
-const { RouteBases, Routes, PermissionFlagsBits } = require('discord-api-types/v10');
-const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
-const { Base } = require('./Base.js');
-const { GuildScheduledEvent } = require('./GuildScheduledEvent.js');
-const { IntegrationApplication } = require('./IntegrationApplication.js');
-
-/**
- * Represents an invitation to a guild channel.
- *
- * @extends {Base}
- */
-class Invite extends Base {
- /**
- * A regular expression that matches Discord invite links.
- * The `code` group property is present on the `exec()` result of this expression.
- *
- * @type {RegExp}
- * @memberof Invite
- */
- static InvitesPattern = /discord(?:(?:app)?\.com\/invite|\.gg(?:\/invite)?)\/(?[\w-]{2,255})/i;
-
- constructor(client, data) {
- super(client);
-
- /**
- * The type of this invite
- *
- * @type {InviteType}
- */
- this.type = data.type;
-
- this._patch(data);
- }
-
- _patch(data) {
- const { InviteGuild } = require('./InviteGuild.js');
- /**
- * The guild the invite is for including welcome screen data if present
- *
- * @type {?(Guild|InviteGuild)}
- */
- this.guild ??= null;
- if (data.guild) {
- this.guild = this.client.guilds.cache.get(data.guild.id) ?? new InviteGuild(this.client, data.guild);
- }
-
- if ('code' in data) {
- /**
- * The code for this invite
- *
- * @type {string}
- */
- this.code = data.code;
- }
-
- if ('approximate_presence_count' in data) {
- /**
- * The approximate number of online members of the guild this invite is for
- * This is only available when the invite was fetched through {@link Client#fetchInvite}.
- *
- * @type {?number}
- */
- this.presenceCount = data.approximate_presence_count;
- } else {
- this.presenceCount ??= null;
- }
-
- if ('approximate_member_count' in data) {
- /**
- * The approximate total number of members of the guild this invite is for
- * This is only available when the invite was fetched through {@link Client#fetchInvite}.
- *
- * @type {?number}
- */
- this.memberCount = data.approximate_member_count;
- } else {
- this.memberCount ??= null;
- }
-
- if ('temporary' in data) {
- /**
- * Whether or not this invite only grants temporary membership
- * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
- * or created through {@link GuildInviteManager#create}.
- *
- * @type {?boolean}
- */
- this.temporary = data.temporary ?? null;
- } else {
- this.temporary ??= null;
- }
-
- if ('max_age' in data) {
- /**
- * The maximum age of the invite, in seconds, 0 if never expires
- * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
- * or created through {@link GuildInviteManager#create}.
- *
- * @type {?number}
- */
- this.maxAge = data.max_age;
- } else {
- this.maxAge ??= null;
- }
-
- if ('uses' in data) {
- /**
- * How many times this invite has been used
- * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
- * or created through {@link GuildInviteManager#create}.
- *
- * @type {?number}
- */
- this.uses = data.uses;
- } else {
- this.uses ??= null;
- }
-
- if ('max_uses' in data) {
- /**
- * The maximum uses of this invite
- * This is only available when the invite was fetched through {@link GuildInviteManager#fetch}
- * or created through {@link GuildInviteManager#create}.
- *
- * @type {?number}
- */
- this.maxUses = data.max_uses;
- } else {
- this.maxUses ??= null;
- }
-
- if ('inviter_id' in data) {
- /**
- * The user's id who created this invite
- *
- * @type {?Snowflake}
- */
- this.inviterId = data.inviter_id;
- } else {
- this.inviterId ??= null;
- }
-
- if ('inviter' in data) {
- this.client.users._add(data.inviter);
- this.inviterId = data.inviter.id;
- }
-
- if ('target_user' in data) {
- /**
- * The user whose stream to display for this voice channel stream invite
- *
- * @type {?User}
- */
- this.targetUser = this.client.users._add(data.target_user);
- } else {
- this.targetUser ??= null;
- }
-
- if ('target_application' in data) {
- /**
- * The embedded application to open for this voice channel embedded application invite
- *
- * @type {?IntegrationApplication}
- */
- this.targetApplication = new IntegrationApplication(this.client, data.target_application);
- } else {
- this.targetApplication ??= null;
- }
-
- if ('target_type' in data) {
- /**
- * The target type
- *
- * @type {?InviteTargetType}
- */
- this.targetType = data.target_type;
- } else {
- this.targetType ??= null;
- }
-
- if ('channel_id' in data) {
- /**
- * The id of the channel this invite is for
- *
- * @type {?Snowflake}
- */
- this.channelId = data.channel_id;
- }
-
- if ('channel' in data) {
- /**
- * The channel this invite is for
- *
- * @type {?BaseChannel}
- */
- this.channel =
- this.client.channels._add(data.channel, this.guild, { cache: false }) ??
- this.client.channels.resolve(this.channelId);
-
- this.channelId ??= data.channel.id;
- }
-
- if ('created_at' in data) {
- /**
- * The timestamp this invite was created at
- *
- * @type {?number}
- */
- this.createdTimestamp = Date.parse(data.created_at);
- } else {
- this.createdTimestamp ??= null;
- }
-
- if ('expires_at' in data) {
- this._expiresTimestamp = data.expires_at && Date.parse(data.expires_at);
- } else {
- this._expiresTimestamp ??= null;
- }
-
- if ('guild_scheduled_event' in data) {
- /**
- * The guild scheduled event data if there is a {@link GuildScheduledEvent} in the channel this invite is for
- *
- * @type {?GuildScheduledEvent}
- */
- this.guildScheduledEvent = new GuildScheduledEvent(this.client, data.guild_scheduled_event);
- } else {
- this.guildScheduledEvent ??= null;
- }
- }
-
- /**
- * The time the invite was created at
- *
- * @type {?Date}
- * @readonly
- */
- get createdAt() {
- return this.createdTimestamp && new Date(this.createdTimestamp);
- }
-
- /**
- * Whether the invite is deletable by the client user
- *
- * @type {boolean}
- * @readonly
- */
- get deletable() {
- const guild = this.guild;
- if (!guild || !this.client.guilds.cache.has(guild.id)) return false;
- if (!guild.members.me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
-
- return (
- this.channel?.permissionsFor(this.client.user).has(PermissionFlagsBits.ManageChannels, false) ||
- guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild)
- );
- }
-
- /**
- * The timestamp the invite will expire at
- *
- * @type {?number}
- * @readonly
- */
- get expiresTimestamp() {
- return (
- this._expiresTimestamp ??
- (this.createdTimestamp && this.maxAge ? this.createdTimestamp + this.maxAge * 1_000 : null)
- );
- }
-
- /**
- * The time the invite will expire at
- *
- * @type {?Date}
- * @readonly
- */
- get expiresAt() {
- return this.expiresTimestamp && new Date(this.expiresTimestamp);
- }
-
- /**
- * The user who created this invite
- *
- * @type {?User}
- * @readonly
- */
- get inviter() {
- return this.inviterId && this.client.users.resolve(this.inviterId);
- }
-
- /**
- * The URL to the invite
- *
- * @type {string}
- * @readonly
- */
- get url() {
- return `${RouteBases.invite}/${this.code}`;
- }
-
- /**
- * Deletes this invite.
- *
- * @param {string} [reason] Reason for deleting this invite
- * @returns {Promise}
- */
- async delete(reason) {
- await this.client.rest.delete(Routes.invite(this.code), { reason });
- return this;
- }
-
- /**
- * When concatenated with a string, this automatically concatenates the invite's URL instead of the object.
- *
- * @returns {string}
- * @example
- * // Logs: Invite: https://discord.gg/A1b2C3
- * console.log(`Invite: ${invite}`);
- */
- toString() {
- return this.url;
- }
-
- toJSON() {
- return super.toJSON({
- url: true,
- expiresTimestamp: true,
- presenceCount: false,
- memberCount: false,
- uses: false,
- channel: 'channelId',
- inviter: 'inviterId',
- guild: 'guildId',
- });
- }
-
- valueOf() {
- return this.code;
- }
-}
-
-exports.Invite = Invite;
diff --git a/packages/discord.js/src/util/DataResolver.js b/packages/discord.js/src/util/DataResolver.js
index 411aba65e..5e2c0adfd 100644
--- a/packages/discord.js/src/util/DataResolver.js
+++ b/packages/discord.js/src/util/DataResolver.js
@@ -5,7 +5,7 @@ const fs = require('node:fs/promises');
const path = require('node:path');
const { fetch } = require('undici');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
-const { Invite } = require('../structures/Invite.js');
+const { BaseInvite } = require('../structures/BaseInvite.js');
/**
* Data that can be resolved to give an invite code. This can be:
@@ -43,7 +43,7 @@ function resolveCode(data, regex) {
* @private
*/
function resolveInviteCode(data) {
- return resolveCode(data, Invite.InvitesPattern);
+ return resolveCode(data, BaseInvite.InvitesPattern);
}
/**
diff --git a/packages/discord.js/src/util/Invites.js b/packages/discord.js/src/util/Invites.js
new file mode 100644
index 000000000..fb68a7b1e
--- /dev/null
+++ b/packages/discord.js/src/util/Invites.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const { InviteType } = require('discord-api-types/v10');
+const { BaseInvite } = require('../structures/BaseInvite.js');
+const { GroupDMInvite } = require('../structures/GroupDMInvite.js');
+const { GuildInvite } = require('../structures/GuildInvite.js');
+
+/**
+ * Any invite.
+ *
+ * @typedef {GuildInvite|GroupDMInvite} Invite
+ */
+
+const InviteTypeToClass = {
+ [InviteType.Guild]: GuildInvite,
+ [InviteType.GroupDM]: GroupDMInvite,
+};
+
+/**
+ * Creates an invite.
+ *
+ * @param {Client} client The client
+ * @param {Object} data The data
+ * @returns {BaseInvite}
+ * @ignore
+ */
+function createInvite(client, data) {
+ return new (InviteTypeToClass[data.type] ?? BaseInvite)(client, data);
+}
+
+exports.createInvite = createInvite;
diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts
index 3198570d3..b7e9dd722 100644
--- a/packages/discord.js/typings/index.d.ts
+++ b/packages/discord.js/typings/index.d.ts
@@ -615,8 +615,8 @@ export class BaseGuildTextChannel extends GuildChannel {
public nsfw: boolean;
public threads: GuildTextThreadManager;
public topic: string | null;
- public createInvite(options?: InviteCreateOptions): Promise;
- public fetchInvites(cache?: boolean): Promise>;
+ public createInvite(options?: InviteCreateOptions): Promise;
+ public fetchInvites(cache?: boolean): Promise>;
public setDefaultAutoArchiveDuration(
defaultAutoArchiveDuration: ThreadAutoArchiveDuration,
reason?: string,
@@ -644,8 +644,8 @@ export class BaseGuildVoiceChannel extends GuildChannel {
public rtcRegion: string | null;
public userLimit: number;
public videoQualityMode: VideoQualityMode | null;
- public createInvite(options?: InviteCreateOptions): Promise;
- public fetchInvites(cache?: boolean): Promise>;
+ public createInvite(options?: InviteCreateOptions): Promise;
+ public fetchInvites(cache?: boolean): Promise>;
public setBitrate(bitrate: number, reason?: string): Promise;
public setRTCRegion(rtcRegion: string | null, reason?: string): Promise;
public setUserLimit(userLimit: number, reason?: string): Promise;
@@ -931,6 +931,10 @@ export class Client extends BaseClient;
public deleteWebhook(id: Snowflake, options?: WebhookDeleteOptions): Promise;
public fetchGuildPreview(guild: GuildResolvable): Promise;
+ public fetchInvite(
+ invite: InviteResolvable,
+ options: ClientFetchInviteOptions & { withCounts: true },
+ ): Promise>;
public fetchInvite(invite: InviteResolvable, options?: ClientFetchInviteOptions): Promise;
public fetchGuildTemplate(template: GuildTemplateResolvable): Promise;
public fetchVoiceRegions(): Promise>;
@@ -2018,37 +2022,49 @@ export class InteractionWebhook {
public fetchMessage(message: Snowflake | '@original'): Promise;
}
-export class Invite extends Base {
- private constructor(client: Client, data: unknown);
- public channel: NonThreadGuildBasedChannel | PartialGroupDMChannel | null;
- public channelId: Snowflake | null;
- public code: string;
- public get deletable(): boolean;
+export class BaseInvite extends Base {
+ protected constructor(client: Client, data: unknown);
+ public readonly type: InviteType;
+ public readonly code: string;
+ public readonly inviterId: Snowflake | null;
+ public get inviter(): User | null;
+ public maxAge: number | null;
public get createdAt(): Date | null;
public createdTimestamp: number | null;
public get expiresAt(): Date | null;
public get expiresTimestamp(): number | null;
- public guild: Guild | InviteGuild | null;
- public get inviter(): User | null;
- public inviterId: Snowflake | null;
- public maxAge: number | null;
- public maxUses: number | null;
- public memberCount: number;
- public presenceCount: number;
- public targetApplication: IntegrationApplication | null;
- public targetUser: User | null;
- public targetType: InviteTargetType | null;
- public temporary: boolean | null;
- public type: InviteType;
+ public readonly channelId: Snowflake | null;
+ public approximateMemberCount: WithCounts extends true ? number : null;
public get url(): string;
- public uses: number | null;
- public delete(reason?: string): Promise;
- public toJSON(): unknown;
- public toString(): string;
public static InvitesPattern: RegExp;
- public guildScheduledEvent: GuildScheduledEvent | null;
+ public toString(): string;
+ public toJSON(): unknown;
}
+export class GuildInvite extends BaseInvite {
+ public readonly type: InviteType.Guild;
+ public guild: Guild | InviteGuild | null;
+ public readonly guildId: Snowflake;
+ public channel: NonThreadGuildBasedChannel | null;
+ public targetType: InviteTargetType | null;
+ public targetUser: User | null;
+ public targetApplication: IntegrationApplication | null;
+ public guildScheduledEvent: GuildScheduledEvent | null;
+ public uses: number | null;
+ public maxUses: number | null;
+ public temporary: boolean | null;
+ public approximatePresenceCount: WithCounts extends true ? number : null;
+ public get deletable(): boolean;
+ public delete(reason?: string): Promise;
+}
+
+export class GroupDMInvite extends BaseInvite {
+ public readonly type: InviteType.GroupDM;
+ public channel: PartialGroupDMChannel | null;
+}
+
+export type Invite = GroupDMInvite | GuildInvite;
+
export class InviteGuild extends AnonymousGuild {
private constructor(client: Client, data: APIPartialGuild);
public welcomeScreen: WelcomeScreen | null;
@@ -2663,8 +2679,8 @@ export abstract class ThreadOnlyChannel extends GuildChannel {
public setAvailableTags(tags: readonly GuildForumTagData[], reason?: string): Promise;
public setDefaultReactionEmoji(emojiId: DefaultReactionEmoji | null, reason?: string): Promise;
public setDefaultThreadRateLimitPerUser(rateLimit: number, reason?: string): Promise;
- public createInvite(options?: InviteCreateOptions): Promise;
- public fetchInvites(cache?: boolean): Promise>;
+ public createInvite(options?: InviteCreateOptions): Promise;
+ public fetchInvites(cache?: boolean): Promise>;
public setDefaultAutoArchiveDuration(
defaultAutoArchiveDuration: ThreadAutoArchiveDuration,
reason?: string,
@@ -4393,13 +4409,13 @@ export class GuildBanManager extends CachedManager;
}
-export class GuildInviteManager extends DataManager {
+export class GuildInviteManager extends DataManager {
private constructor(guild: Guild, iterable?: Iterable);
public guild: Guild;
- public create(channel: GuildInvitableChannelResolvable, options?: InviteCreateOptions): Promise;
- public fetch(options: FetchInviteOptions | InviteResolvable): Promise;
- public fetch(options?: FetchInvitesOptions): Promise>;
- public delete(invite: InviteResolvable, reason?: string): Promise;
+ public create(channel: GuildInvitableChannelResolvable, options?: InviteCreateOptions): Promise;
+ public fetch(options: FetchInviteOptions | InviteResolvable): Promise;
+ public fetch(options?: FetchInvitesOptions): Promise>;
+ public delete(invite: InviteResolvable, reason?: string): Promise;
}
export class GuildScheduledEventManager extends CachedManager<
@@ -5206,7 +5222,7 @@ export interface Caches {
// TODO: GuildChannelManager: [manager: typeof GuildChannelManager, holds: typeof GuildChannel];
GuildEmojiManager: [manager: typeof GuildEmojiManager, holds: typeof GuildEmoji];
GuildForumThreadManager: [manager: typeof GuildForumThreadManager, holds: typeof ThreadChannel];
- GuildInviteManager: [manager: typeof GuildInviteManager, holds: typeof Invite];
+ GuildInviteManager: [manager: typeof GuildInviteManager, holds: typeof GuildInvite];
// TODO: GuildManager: [manager: typeof GuildManager, holds: typeof Guild];
GuildMemberManager: [manager: typeof GuildMemberManager, holds: typeof GuildMember];
GuildMessageManager: [manager: typeof GuildMessageManager, holds: typeof Message];
@@ -5367,8 +5383,8 @@ export interface ClientEventTypes {
guildUpdate: [oldGuild: Guild, newGuild: Guild];
interactionCreate: [interaction: Interaction];
invalidated: [];
- inviteCreate: [invite: Invite];
- inviteDelete: [invite: Invite];
+ inviteCreate: [invite: GuildInvite];
+ inviteDelete: [invite: GuildInvite];
messageCreate: [message: OmitPartialGroupDMChannel];
messageDelete: [message: OmitPartialGroupDMChannel];
messageDeleteBulk: [
@@ -5430,6 +5446,7 @@ export interface ClientEventTypes {
export interface ClientFetchInviteOptions {
guildScheduledEventId?: Snowflake;
+ withCounts?: boolean;
}
export interface ClientOptions extends WebhookClientOptions {
@@ -5989,7 +6006,7 @@ export interface GuildAuditLogsEntryTargetField {
GuildOnboardingPrompt: GuildOnboardingPrompt | { [x: string]: unknown; id: Snowflake };
GuildScheduledEvent: GuildScheduledEvent;
Integration: Integration;
- Invite: Invite;
+ Invite: GuildInvite;
Message: TAction extends AuditLogEvent.MessageBulkDelete ? GuildTextBasedChannel | { id: Snowflake } : User | null;
Role: Role | { id: Snowflake };
SoundboardSound: SoundboardSound | { id: Snowflake };
@@ -6176,7 +6193,14 @@ export interface GuildMemberEditOptions {
roles?: ReadonlyCollection | readonly RoleResolvable[];
}
-export type GuildResolvable = Guild | GuildEmoji | GuildMember | Invite | NonThreadGuildBasedChannel | Role | Snowflake;
+export type GuildResolvable =
+ | Guild
+ | GuildEmoji
+ | GuildInvite
+ | GuildMember
+ | NonThreadGuildBasedChannel
+ | Role
+ | Snowflake;
export interface GuildPruneMembersOptions {
count?: boolean;
@@ -6411,14 +6435,9 @@ export interface InviteGenerationOptions {
scopes: readonly OAuth2Scopes[];
}
-export type GuildInvitableChannelResolvable =
- | AnnouncementChannel
- | ForumChannel
- | MediaChannel
- | Snowflake
- | StageChannel
- | TextChannel
- | VoiceChannel;
+export type GuildInvitableChannel = AnnouncementChannel | ForumChannel | MediaChannel | TextChannel | VoiceChannel;
+
+export type GuildInvitableChannelResolvable = GuildInvitableChannel | Snowflake;
export interface InviteCreateOptions {
maxAge?: number;
@@ -6432,6 +6451,7 @@ export interface InviteCreateOptions {
}
export type InviteResolvable = string;
+export type GuildInviteResolvable = string;
export interface LifetimeFilterOptions {
excludeFromSweep?(value: Value, key: Key, collection: LimitedCollection): boolean;
@@ -7004,7 +7024,7 @@ export interface SweeperDefinitions {
emojis: [Snowflake, GuildEmoji];
entitlements: [Snowflake, Entitlement];
guildMembers: [Snowflake, GuildMember];
- invites: [string, Invite, true];
+ invites: [string, GuildInvite, true];
messages: [Snowflake, Message, true];
presences: [Snowflake, Presence];
reactions: [Snowflake | string, MessageReaction];
diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts
index 7f2ea9e9d..58de5134e 100644
--- a/packages/discord.js/typings/index.test-d.ts
+++ b/packages/discord.js/typings/index.test-d.ts
@@ -199,6 +199,8 @@ import type {
User,
VoiceBasedChannel,
VoiceChannel,
+ Invite,
+ GuildInvite,
} from './index.js';
import {
ActionRowBuilder,
@@ -262,6 +264,10 @@ if (client.isReady()) {
expectType(client);
}
+expectType>(client.fetchInvite('https://discord.gg/djs'));
+expectType>>(client.fetchInvite('https://discord.gg/djs', { withCounts: true }));
+expectNotType>>(client.fetchInvite('https://discord.gg/djs', { withCounts: false }));
+
const testGuildId = '222078108977594368'; // DJS
const testUserId = '987654321098765432'; // example id
const globalCommandId = '123456789012345678'; // example id
@@ -407,8 +413,15 @@ client.on('interactionCreate', async interaction => {
}
});
-client.on('inviteCreate', ({ client }) => expectType>(client));
-client.on('inviteDelete', ({ client }) => expectType>(client));
+client.on('inviteCreate', invite => {
+ expectType(invite);
+ expectType>(invite.client);
+});
+
+client.on('inviteDelete', invite => {
+ expectType(invite);
+ expectType>(invite.client);
+});
// This is to check that stuff is the right type
declare const assertIsMessage: (m: Promise) => void;