UNPKG

@discordjs/structures

Version:

Wrapper around Discord's structures

1,884 lines (1,840 loc) 49.6 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/bitfields/BitField.ts var BitField = class _BitField { static { __name(this, "BitField"); } /** * Numeric bit field flags. * * @remarks Defined in extension classes */ static Flags = {}; static DefaultBit = 0n; /** * Bitfield of the packed bits */ bitField; /** * @param bits - Bit(s) to read from */ constructor(bits = this.constructor.DefaultBit) { this.bitField = this.constructor.resolve(bits); } /** * Checks whether the bit field has a bit, or any of multiple bits. * * @param bit - Bit(s) to check for * @returns Whether the bit field has the bit(s) */ any(bit) { return (this.bitField & this.constructor.resolve(bit)) !== this.constructor.DefaultBit; } /** * Checks if this bit field equals another * * @param bit - Bit(s) to check for * @returns Whether this bit field equals the other */ equals(bit) { return this.bitField === this.constructor.resolve(bit); } /** * Checks whether the bit field has a bit, or multiple bits. * * @param bit - Bit(s) to check for * @returns Whether the bit field has the bit(s) */ has(bit, ..._hasParams) { const resolvedBit = this.constructor.resolve(bit); return (this.bitField & resolvedBit) === resolvedBit; } /** * Gets all given bits that are missing from the bit field. * * @param bits - Bit(s) to check for * @param hasParams - Additional parameters for the has method, if any * @returns A bit field containing the missing bits */ missing(bits, ...hasParams) { return new this.constructor(bits).remove(this).toArray(...hasParams); } /** * Freezes these bits, making them immutable. * * @returns This bit field but frozen */ freeze() { return Object.freeze(this); } /** * Adds bits to these ones. * * @param bits - Bits to add * @returns These bits or new BitField if the instance is frozen. */ add(...bits) { let total = this.constructor.DefaultBit; for (const bit of bits) { total |= this.constructor.resolve(bit); } if (Object.isFrozen(this)) return new this.constructor(this.bitField | total); this.bitField |= total; return this; } /** * Removes bits from these. * * @param bits - Bits to remove * @returns These bits or new BitField if the instance is frozen. */ remove(...bits) { let total = this.constructor.DefaultBit; for (const bit of bits) { total |= this.constructor.resolve(bit); } if (Object.isFrozen(this)) return new this.constructor(this.bitField & ~total); this.bitField &= ~total; return this; } /** * Gets an object mapping field names to a boolean indicating whether the bit is available. * * @param hasParams - Additional parameters for the has method, if any * @returns An object mapping field names to a boolean indicating whether the bit is available */ serialize(...hasParams) { const serialized = {}; for (const [flag, bit] of Object.entries(this.constructor.Flags)) { if (Number.isNaN(Number(flag))) serialized[flag] = this.has(bit, ...hasParams); } return serialized; } /** * Gets an Array of bit field names based on the bits available. * * @param hasParams - Additional parameters for the has method, if any * @returns An Array of bit field names */ toArray(...hasParams) { return [...this[Symbol.iterator](...hasParams)]; } toJSON(asNumber) { if (asNumber) { if (this.bitField > Number.MAX_SAFE_INTEGER) { throw new RangeError( `Cannot convert bitfield value ${this.bitField} to number, as it is bigger than ${Number.MAX_SAFE_INTEGER} (the maximum safe integer)` ); } return Number(this.bitField); } return this.bitField.toString(); } valueOf() { return this.bitField; } *[Symbol.iterator](...hasParams) { for (const bitName of Object.keys(this.constructor.Flags)) { if (Number.isNaN(Number(bitName)) && this.has(bitName, ...hasParams)) yield bitName; } } /** * Resolves bit fields to their numeric form. * * @param bit - bit(s) to resolve * @returns the numeric value of the bit fields */ static resolve(bit) { const DefaultBit = this.DefaultBit; if (typeof bit === "bigint" && bit >= DefaultBit) return bit; if (typeof bit === "number" && BigInt(bit) >= DefaultBit) return BigInt(bit); if (bit instanceof _BitField) return bit.bitField; if (Array.isArray(bit)) { return bit.map((bit_) => this.resolve(bit_)).reduce((prev, bit_) => prev | bit_, DefaultBit); } if (typeof bit === "string") { if (!Number.isNaN(Number(bit))) return BigInt(bit); if (bit in this.Flags) return this.Flags[bit]; } throw new Error(`BitFieldInvalid: ${JSON.stringify(bit)}`); } }; // src/bitfields/ChannelFlagsBitField.ts import { ChannelFlags } from "discord-api-types/v10"; var ChannelFlagsBitField = class extends BitField { static { __name(this, "ChannelFlagsBitField"); } /** * Numeric guild channel flags. */ static Flags = ChannelFlags; toJSON() { return super.toJSON(true); } }; // src/bitfields/PermissionsBitField.ts import { PermissionFlagsBits } from "discord-api-types/v10"; var PermissionsBitField = class extends BitField { static { __name(this, "PermissionsBitField"); } /** * Numeric permission flags. * * @see {@link https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags} */ static Flags = PermissionFlagsBits; /** * Bit field representing every permission combined */ static All = Object.values(PermissionFlagsBits).reduce((all, perm) => all | perm, 0n); /** * Bit field representing the default permissions for users */ static Default = 104324673n; /** * Bit field representing the permissions required for moderators of stage channels */ static StageModerator = PermissionFlagsBits.ManageChannels | PermissionFlagsBits.MuteMembers | PermissionFlagsBits.MoveMembers; /** * Gets all given bits that are missing from the bit field. * * @param bits - Bit(s) to check for * @param checkAdmin - Whether to allow the administrator permission to override * @returns A bit field containing the missing permissions */ missing(bits, checkAdmin = true) { return checkAdmin && this.has(PermissionFlagsBits.Administrator) ? [] : super.missing(bits); } /** * Checks whether the bit field has a permission, or any of multiple permissions. * * @param permission - Permission(s) to check for * @param checkAdmin - Whether to allow the administrator permission to override * @returns Whether the bit field has the permission(s) */ any(permission, checkAdmin = true) { return checkAdmin && super.has(PermissionFlagsBits.Administrator) || super.any(permission); } /** * Checks whether the bit field has a permission, or multiple permissions. * * @param permission - Permission(s) to check for * @param checkAdmin - Whether to allow the administrator permission to override * @returns Whether the bit field has the permission(s) */ has(permission, checkAdmin = true) { return checkAdmin && super.has(PermissionFlagsBits.Administrator) || super.has(permission); } /** * Gets an Array of bitfield names based on the permissions available. * * @returns An Array of permission names */ toArray() { return super.toArray(false); } }; // src/utils/symbols.ts var kData = Symbol.for("djs.structures.data"); var kClone = Symbol.for("djs.structures.clone"); var kPatch = Symbol.for("djs.structures.patch"); var kExpiresTimestamp = Symbol.for("djs.structures.expiresTimestamp"); var kCreatedTimestamp = Symbol.for("djs.structures.createdTimestamp"); var kEditedTimestamp = Symbol.for("djs.structures.editedTimestamp"); var kArchiveTimestamp = Symbol.for("djs.structures.archiveTimestamp"); var kAllow = Symbol.for("djs.structures.allow"); var kDeny = Symbol.for("djs.structures.deny"); var kLastPinTimestamp = Symbol.for("djs.structures.lastPinTimestamp"); var kMixinConstruct = Symbol.for("djs.structures.mixin.construct"); var kMixinToJSON = Symbol.for("djs.structures.mixin.toJSON"); // src/channels/mixins/AppliedTagsMixin.ts var AppliedTagsMixin = class { static { __name(this, "AppliedTagsMixin"); } /** * The ids of the set of tags that have been applied to a thread in a {@link (ForumChannel:class)} or a {@link (MediaChannel:class)}. */ get appliedTags() { return Array.isArray(this[kData].applied_tags) ? this[kData].applied_tags : null; } }; // src/channels/mixins/ChannelOwnerMixin.ts var ChannelOwnerMixin = class { static { __name(this, "ChannelOwnerMixin"); } /** * The id of the creator of the group DM or thread */ get ownerId() { return this[kData].owner_id; } }; // src/channels/mixins/GuildChannelMixin.ts import { channelLink } from "@discordjs/formatters"; var GuildChannelMixin = class { static { __name(this, "GuildChannelMixin"); } /** * The flags that are applied to the channel. * * @privateRemarks The type of `flags` can be narrowed in Guild Channels and DMChannel to ChannelFlags, and in GroupDM channel * to null, respecting Omit behaviors */ get flags() { return this[kData].flags ? new ChannelFlagsBitField(this[kData].flags) : null; } /** * THe id of the guild this channel is in. */ get guildId() { return this[kData].guild_id; } /** * The URL to this channel. */ get url() { return channelLink(this.id, this.guildId); } /** * Indicates whether this channel is in a guild */ isGuildBased() { return true; } }; // src/channels/mixins/ChannelParentMixin.ts var ChannelParentMixin = class extends GuildChannelMixin { static { __name(this, "ChannelParentMixin"); } /** * The id of the parent category for a channel (each parent category can contain up to 50 channels) or id of the parent channel for a thread */ get parentId() { return this[kData].parent_id; } /** * Whether the channel is nsfw */ get nsfw() { return this[kData].nsfw; } }; // src/channels/mixins/ChannelPermissionMixin.ts var ChannelPermissionMixin = class { static { __name(this, "ChannelPermissionMixin"); } /** * The sorting position of the channel */ get position() { return this[kData].position; } /** * Indicates whether this channel can have permission overwrites */ isPermissionCapable() { return true; } }; // src/channels/mixins/ChannelPinMixin.ts var ChannelPinMixin = class { static { __name(this, "ChannelPinMixin"); } /** * The template used for removing data from the raw data stored for each Channel. */ static DataTemplate = { set last_pin_timestamp(_) { } }; [kMixinConstruct]() { this[kLastPinTimestamp] ??= null; } /** * {@inheritDoc Structure.optimizeData} */ optimizeData(data) { if (data.last_pin_timestamp) { this[kLastPinTimestamp] = Date.parse(data.last_pin_timestamp); } } /** * The timestamp of when the last pin in the channel happened. */ get lastPinTimestamp() { return this[kLastPinTimestamp]; } /** * The Date of when the last pin in the channel happened */ get lastPinAt() { const lastPinTimestamp = this.lastPinTimestamp; return lastPinTimestamp ? new Date(lastPinTimestamp) : null; } /** * Adds data from optimized properties omitted from [kData]. * * @param data - the result of {@link (Structure:class).toJSON} */ [kMixinToJSON](data) { data.last_pin_timestamp = this[kLastPinTimestamp] ? new Date(this[kLastPinTimestamp]).toISOString() : null; } }; // src/channels/mixins/TextChannelMixin.ts var TextChannelMixin = class { static { __name(this, "TextChannelMixin"); } /** * The id of the last message sent in this channel. */ get lastMessageId() { return this[kData].last_message_id; } /** * Indicates whether this channel can contain messages */ isTextBased() { return true; } }; // src/channels/mixins/ChannelSlowmodeMixin.ts var ChannelSlowmodeMixin = class extends TextChannelMixin { static { __name(this, "ChannelSlowmodeMixin"); } /** * The rate limit per user (slowmode) of this channel. */ get rateLimitPerUser() { return this[kData].rate_limit_per_user; } }; // src/channels/mixins/ChannelWebhookMixin.ts var ChannelWebhookMixin = class { static { __name(this, "ChannelWebhookMixin"); } /** * Indicates whether this channel can have webhooks */ isWebhookCapable() { return true; } }; // src/channels/mixins/ChannelTopicMixin.ts var ChannelTopicMixin = class extends ChannelWebhookMixin { static { __name(this, "ChannelTopicMixin"); } /** * The topic of this channel. */ get topic() { return this[kData].topic; } /** * The duration after which new threads get archived by default on this channel. */ get defaultAutoArchiveDuration() { return this[kData].default_auto_archive_duration; } /** * The default value for rate limit per user (slowmode) on new threads in this channel. */ get defaultThreadRateLimitPerUser() { return this[kData].default_thread_rate_limit_per_user; } }; // src/channels/mixins/DMChannelMixin.ts import { channelLink as channelLink2 } from "@discordjs/formatters"; var DMChannelMixin = class { static { __name(this, "DMChannelMixin"); } /** * The URL to this channel. */ get url() { return channelLink2(this.id); } /** * Indicates whether this channel is a DM or DM Group */ isDMBased() { return true; } }; // src/channels/mixins/GroupDMMixin.ts var GroupDMMixin = class { static { __name(this, "GroupDMMixin"); } /** * The icon hash of the group DM. */ get icon() { return this[kData].icon; } /** * Whether the channel is managed by an application via the `gdm.join` OAuth2 scope. */ get managed() { return this[kData].managed; } /** * The application id of the group DM creator if it is bot-created. */ get applicationId() { return this[kData].application_id; } }; // src/channels/mixins/ThreadChannelMixin.ts var ThreadChannelMixin = class { static { __name(this, "ThreadChannelMixin"); } /** * The approximate count of users in a thread, stops counting at 50 */ get memberCount() { return this[kData].member_count; } /** * The number of messages (not including the initial message or deleted messages) in a thread. */ get messageCount() { return this[kData].message_count; } /** * The number of messages ever sent in a thread, it's similar to message_count on message creation, * but will not decrement the number when a message is deleted. */ get totalMessageSent() { return this[kData].total_message_sent; } /** * Indicates whether this channel is a thread channel */ isThread() { return true; } }; // src/channels/mixins/ThreadOnlyChannelMixin.ts var ThreadOnlyChannelMixin = class { static { __name(this, "ThreadOnlyChannelMixin"); } /** * The emoji to show in the add reaction button on a thread in this channel. */ get defaultReactionEmoji() { return this[kData].default_reaction_emoji; } /** * The default sort order type used to order posts in this channel. * * @defaultValue `null` – indicates a preferred sort order hasn't been set. */ get defaultSortOrder() { return this[kData].default_sort_order; } /** * Indicates whether this channel only allows thread creation */ isThreadOnly() { return true; } }; // src/channels/mixins/VoiceChannelMixin.ts var VoiceChannelMixin = class extends TextChannelMixin { static { __name(this, "VoiceChannelMixin"); } /** * The bitrate (in bits) of the voice channel. */ get bitrate() { return this[kData].bitrate; } /** * The voice region id for this channel, automatic when set to null. */ get rtcRegion() { return this[kData].rtc_region; } /** * The camera video quality mode of the voice channel, {@link discord-api-types/v10#(VideoQualityMode:enum) | Auto} when not present. */ get videoQualityMode() { return this[kData].video_quality_mode; } /** * The user limit of the voice channel. */ get userLimit() { return this[kData].user_limit; } /** * Indicates whether this channel has voice connection capabilities */ isVoiceBased() { return true; } }; // src/Structure.ts var DataTemplatePropertyName = "DataTemplate"; var OptimizeDataPropertyName = "optimizeData"; var Structure = class { static { __name(this, "Structure"); } /** * The template used for removing data from the raw data stored for each Structure. * * @remarks This template should be overridden in all subclasses to provide more accurate type information. * The template in the base {@link Structure} class will have no effect on most subclasses for this reason. */ static DataTemplate = {}; /** * @returns A cloned version of the data template, ready to create a new data object. */ getDataTemplate() { return Object.create(this.constructor.DataTemplate); } /** * The raw data from the API for this structure * * @internal */ [kData]; /** * Creates a new structure to represent API data * * @param data - the data from the API that this structure will represent * @remarks To be made public in subclasses * @internal */ constructor(data, ..._rest) { this[kData] = Object.assign(this.getDataTemplate(), data); this[kMixinConstruct]?.(data); } /** * Patches the raw data of this object in place * * @param data - the updated data from the API to patch with * @remarks To be made public in subclasses * @returns this * @internal */ [kPatch](data) { this[kData] = Object.assign(this.getDataTemplate(), this[kData], data); this.optimizeData(data); return this; } /** * Creates a clone of this structure * * @returns a clone of this * @internal */ [kClone](patchPayload) { const clone = this.toJSON(); return new this.constructor( // Ensure the ts-expect-error only applies to the constructor call patchPayload ? Object.assign(clone, patchPayload) : clone ); } /** * Function called to ensure stored raw data is in optimized formats, used in tandem with a data template * * @example created_timestamp is an ISO string, this can be stored in optimized form as a number * @param _data - the raw data received from the API to optimize * @remarks Implementation to be done in subclasses and mixins where needed. * For typescript users, mixins must use the closest ancestors access modifier. * @remarks Automatically called in Structure[kPatch] but must be called manually in the constructor * of any class implementing this method. * @remarks Additionally, when implementing, ensure to call `super._optimizeData` if any class in the super chain aside * from Structure contains an implementation. * Note: mixins do not need to call super ever as the process of mixing walks the prototype chain. * @virtual * @internal */ optimizeData(_data) { } /** * Transforms this object to its JSON format with raw API data (or close to it), * automatically called by `JSON.stringify()` when this structure is stringified * * @remarks * The type of this data is determined by omissions at runtime and is only guaranteed for default omissions * @privateRemarks * When omitting properties at the library level, this must be overridden to re-add those properties */ toJSON() { const data = ( // Spread is way faster than structuredClone, but is shallow. So use it only if there is no nested objects Object.values(this[kData]).some((value) => typeof value === "object" && value !== null) ? structuredClone(this[kData]) : { ...this[kData] } ); this[kMixinToJSON]?.(data); return data; } }; // src/channels/ForumTag.ts var ForumTag = class extends Structure { static { __name(this, "ForumTag"); } constructor(data) { super(data); } /** * The id of the tag. */ get id() { return this[kData].id; } /** * The name of the tag. */ get name() { return this[kData].name; } /** * Whether this tag can only be added to or removed from threads by a member with the {@link discord-api-types/v10#(PermissionFlagsBits:variable) | ManageThreads} permission. */ get moderated() { return this[kData].moderated; } /** * The id of a guild's custom emoji. */ get emojiId() { return this[kData].emoji_id; } /** * The unicode character of the emoji. */ get emojiName() { return this[kData].emoji_name; } /** * The textual representation of this tag's emoji. Either a unicode character or a guild emoji mention. */ get emoji() { return this.emojiName ?? `<:_:${this.emojiId}>`; } }; // src/channels/PermissionOverwrite.ts var PermissionOverwrite = class extends Structure { static { __name(this, "PermissionOverwrite"); } [kAllow] = null; [kDeny] = null; constructor(data) { super(data); this.optimizeData(data); } /** * The template used for removing data from the raw data stored for each ThreadMetadata * * @remarks This template has defaults, if you want to remove additional data and keep the defaults, * use `Object.defineProperties`. To override the defaults, set this value directly. */ static DataTemplate = { set allow(_) { }, set deny(_) { } }; /** * {@inheritDoc Structure.optimizeData} */ optimizeData(data) { if (data.allow) { this[kAllow] = BigInt(data.allow); } if (data.deny) { this[kDeny] = BigInt(data.deny); } } /** * The permission bit set allowed by this overwrite. */ get allow() { const allow = this[kAllow]; return typeof allow === "bigint" ? new PermissionsBitField(allow) : null; } /** * The permission bit set denied by this overwrite. */ get deny() { const deny = this[kDeny]; return typeof deny === "bigint" ? new PermissionsBitField(deny) : null; } /** * The role or user id for this overwrite. */ get id() { return this[kData].id; } /** * The type of this overwrite. */ get type() { return this[kData].type; } /** * {@inheritDoc Structure.toJSON} */ toJSON() { const clone = super.toJSON(); if (this[kAllow]) { clone.allow = this[kAllow].toString(); } if (this[kDeny]) { clone.deny = this[kDeny].toString(); } return clone; } }; // src/channels/ThreadMetadata.ts var ThreadMetadata = class extends Structure { static { __name(this, "ThreadMetadata"); } [kArchiveTimestamp] = null; [kCreatedTimestamp] = null; constructor(data) { super(data); this.optimizeData(data); } /** * The template used for removing data from the raw data stored for each ThreadMetadata * * @remarks This template has defaults, if you want to remove additional data and keep the defaults, * use `Object.defineProperties`. To override the defaults, set this value directly. */ static DataTemplate = { set create_timestamp(_) { }, set archive_timestamp(_) { } }; /** * {@inheritDoc Structure.optimizeData} */ optimizeData(data) { if (data.create_timestamp) { this[kCreatedTimestamp] = Date.parse(data.create_timestamp); } if (data.archive_timestamp) { this[kArchiveTimestamp] = Date.parse(data.archive_timestamp); } } /** * Whether the thread is archived. */ get archived() { return this[kData].archived; } /** * The timestamp when the thread's archive status was last changed, used for calculating recent activity. */ get archivedTimestamp() { return this[kArchiveTimestamp]; } /** * The timestamp when the thread was created; only populated for threads created after 2022-01-09. */ get createdTimestamp() { return this[kCreatedTimestamp]; } /** * The thread will stop showing in the channel list after auto_archive_duration minutes of inactivity, */ get autoArchiveDuration() { return this[kData].auto_archive_duration; } /** * Whether non-moderators can add other non-moderators to a thread; only available on private threads. */ get invitable() { return this[kData].invitable; } /** * Whether the thread is locked; when a thread is locked, only users with {@link discord-api-types/v10#(PermissionFlagsBits:variable) | ManageThreads} can unarchive it. */ get locked() { return this[kData].locked; } /** * The time the thread was archived at */ get archivedAt() { const archivedTimestamp = this.archivedTimestamp; return archivedTimestamp ? new Date(archivedTimestamp) : null; } /** * The time the thread was created at */ get createdAt() { const createdTimestamp = this.createdTimestamp; return createdTimestamp ? new Date(createdTimestamp) : null; } /** * {@inheritDoc Structure.toJSON} */ toJSON() { const data = super.toJSON(); if (this[kArchiveTimestamp]) { data.archive_timestamp = new Date(this[kArchiveTimestamp]).toISOString(); } if (this[kCreatedTimestamp]) { data.create_timestamp = new Date(this[kCreatedTimestamp]).toISOString(); } return data; } }; // src/channels/Channel.ts import { DiscordSnowflake } from "@sapphire/snowflake"; // src/utils/type-guards.ts function isIdSet(id) { return typeof id === "string" || typeof id === "bigint"; } __name(isIdSet, "isIdSet"); // src/channels/Channel.ts var Channel = class extends Structure { static { __name(this, "Channel"); } /** * The template used for removing data from the raw data stored for each Channel. * * @remarks This template is only guaranteed to apply to channels constructed directly via `new Channel()`. * Use the appropriate subclass template to remove data from that channel type. */ static DataTemplate = {}; /** * @param data - The raw data received from the API for the channel */ constructor(data) { super(data); } /** * {@inheritDoc Structure.[kPatch]} * * @internal */ [kPatch](data) { return super[kPatch](data); } /** * The id of the channel */ get id() { return this[kData].id; } /** * The type of the channel */ get type() { return this[kData].type; } /** * The name of the channel, null for DMs * * @privateRemarks The type of `name` can be narrowed in Guild Channels and DM channels to string and null respectively, * respecting Omit behaviors */ get name() { return this[kData].name; } /** * The flags that are applied to the channel. * * @privateRemarks The type of `flags` can be narrowed in Guild Channels and DMChannel to ChannelFlags, and in GroupDM channel * to null, respecting Omit behaviors */ get flags() { const flags = "flags" in this[kData] && typeof this[kData].flags === "number" ? this[kData].flags : null; return flags ? new ChannelFlagsBitField(flags) : null; } /** * The timestamp the channel was created at */ get createdTimestamp() { return isIdSet(this.id) ? DiscordSnowflake.timestampFrom(this.id) : null; } /** * The time the channel was created at */ get createdAt() { const createdTimestamp = this.createdTimestamp; return createdTimestamp ? new Date(createdTimestamp) : null; } /** * Indicates whether this channel is a thread channel * * @privateRemarks Overridden to `true` on `ThreadChannelMixin` */ isThread() { return false; } /** * Indicates whether this channel can contain messages * * @privateRemarks Overridden to `true` on `TextChannelMixin` */ isTextBased() { return false; } /** * Indicates whether this channel is in a guild * * @privateRemarks Overridden to `true` on `GuildChannelMixin` */ isGuildBased() { return false; } /** * Indicates whether this channel is a DM or DM Group * * @privateRemarks Overridden to `true` on `DMChannelMixin` */ isDMBased() { return false; } /** * Indicates whether this channel has voice connection capabilities * * @privateRemarks Overridden to `true` on `VoiceChannelMixin` */ isVoiceBased() { return false; } /** * Indicates whether this channel only allows thread creation * * @privateRemarks Overridden to `true` on `ThreadOnlyChannelMixin` */ isThreadOnly() { return false; } /** * Indicates whether this channel can have permission overwrites * * @privateRemarks Overridden to `true` on `ChannelPermissionsMixin` */ isPermissionCapable() { return false; } /** * Indicates whether this channel can have webhooks * * @privateRemarks Overridden to `true` on `ChannelWebhooksMixin` */ isWebhookCapable() { return false; } }; // src/Mixin.ts function Mixin(destination, mixins) { const dataTemplates = []; const dataOptimizations = []; const enrichToJSONs = []; const constructors = []; for (const mixin of mixins) { const prototypeChain = []; let extendedClass = mixin; while (extendedClass.prototype !== void 0) { if (DataTemplatePropertyName in extendedClass && typeof extendedClass.DataTemplate === "object" && // eslint-disable-next-line no-eq-null, eqeqeq extendedClass.DataTemplate != null) { dataTemplates.push(extendedClass.DataTemplate); } prototypeChain.unshift(extendedClass.prototype); extendedClass = Object.getPrototypeOf(extendedClass); } for (const prototype of prototypeChain) { if (prototype[kMixinConstruct]) { constructors.push(prototype[kMixinConstruct]); } if (prototype[kMixinToJSON]) { enrichToJSONs.push(prototype[kMixinToJSON]); } const originalDescriptors = Object.getOwnPropertyDescriptors(prototype); const usingDescriptors = {}; for (const [prop, descriptor] of Object.entries(originalDescriptors)) { if (["constructor"].includes(prop)) { continue; } if (prop === OptimizeDataPropertyName) { if (typeof descriptor.value !== "function") throw new RangeError(`Expected ${prop} to be a function, received ${typeof descriptor.value} instead.`); dataOptimizations.push(descriptor.value); continue; } if (typeof descriptor.get !== "undefined" || typeof descriptor.set !== "undefined" || typeof descriptor.value === "function") { usingDescriptors[prop] = descriptor; } } Object.defineProperties(destination.prototype, usingDescriptors); } } if (constructors.length > 0) { Object.defineProperty(destination.prototype, kMixinConstruct, { writable: true, enumerable: false, configurable: true, // eslint-disable-next-line func-name-matching value: /* @__PURE__ */ __name(function _mixinConstructors(data) { for (const construct of constructors) { construct.call(this, data); } }, "_mixinConstructors") }); } const baseOptimize = Object.getOwnPropertyDescriptor(destination, OptimizeDataPropertyName); if (baseOptimize && typeof baseOptimize.value === "function") { dataOptimizations.push(baseOptimize.value); } const superOptimize = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(destination).prototype, OptimizeDataPropertyName ); if (!baseOptimize && superOptimize && typeof superOptimize.value === "function") { dataOptimizations.unshift(superOptimize.value); } if (dataOptimizations.length > 1 || dataOptimizations.length === 1 && !baseOptimize) { Object.defineProperty(destination.prototype, OptimizeDataPropertyName, { writable: true, enumerable: false, configurable: true, // eslint-disable-next-line func-name-matching value: /* @__PURE__ */ __name(function _mixinOptimizeData(data) { for (const optimization of dataOptimizations) { optimization.call(this, data); } }, "_mixinOptimizeData") }); } if (enrichToJSONs.length > 0) { Object.defineProperty(destination.prototype, kMixinToJSON, { writable: true, enumerable: false, configurable: true, // eslint-disable-next-line func-name-matching value: /* @__PURE__ */ __name(function _mixinToJSON(data) { for (const enricher of enrichToJSONs) { enricher.call(this, data); } }, "_mixinToJSON") }); } if (dataTemplates.length > 0) { if (!Object.getOwnPropertyDescriptor(destination, DataTemplatePropertyName)) { Object.defineProperty(destination, DataTemplatePropertyName, { value: Object.defineProperties({}, Object.getOwnPropertyDescriptors(destination[DataTemplatePropertyName])), writable: true, enumerable: true, configurable: true }); } for (const template of dataTemplates) { Object.defineProperties(destination[DataTemplatePropertyName], Object.getOwnPropertyDescriptors(template)); } } } __name(Mixin, "Mixin"); // src/channels/AnnouncementChannel.ts var AnnouncementChannel = class extends Channel { static { __name(this, "AnnouncementChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(AnnouncementChannel, [ TextChannelMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelPinMixin, ChannelSlowmodeMixin, ChannelTopicMixin ]); // src/channels/AnnouncementThreadChannel.ts var AnnouncementThreadChannel = class extends Channel { static { __name(this, "AnnouncementThreadChannel"); } constructor(data) { super(data); this.optimizeData?.(data); } }; Mixin(AnnouncementThreadChannel, [ TextChannelMixin, ChannelOwnerMixin, ChannelParentMixin, ChannelPinMixin, ChannelSlowmodeMixin, GuildChannelMixin, ThreadChannelMixin ]); // src/channels/CategoryChannel.ts var CategoryChannel = class extends Channel { static { __name(this, "CategoryChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(CategoryChannel, [ChannelPermissionMixin, GuildChannelMixin]); // src/channels/DMChannel.ts var DMChannel = class extends Channel { static { __name(this, "DMChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(DMChannel, [DMChannelMixin, TextChannelMixin, ChannelPinMixin]); // src/channels/ForumChannel.ts var ForumChannel = class extends Channel { static { __name(this, "ForumChannel"); } constructor(data) { super(data); this.optimizeData(data); } /** * The default forum layout view used to display posts in this channel. * Defaults to 0, which indicates a layout view has not been set by a channel admin. */ get defaultForumLayout() { return this[kData].default_forum_layout; } }; Mixin(ForumChannel, [ChannelParentMixin, ChannelPermissionMixin, ChannelTopicMixin, ThreadOnlyChannelMixin]); // src/channels/GroupDMChannel.ts var GroupDMChannel = class extends Channel { static { __name(this, "GroupDMChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(GroupDMChannel, [DMChannelMixin, TextChannelMixin, ChannelOwnerMixin, GroupDMMixin]); // src/channels/MediaChannel.ts var MediaChannel = class extends Channel { static { __name(this, "MediaChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(MediaChannel, [ChannelParentMixin, ChannelPermissionMixin, ChannelTopicMixin, ThreadOnlyChannelMixin]); // src/channels/PrivateThreadChannel.ts var PrivateThreadChannel = class extends Channel { static { __name(this, "PrivateThreadChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(PrivateThreadChannel, [ TextChannelMixin, ChannelOwnerMixin, ChannelParentMixin, ChannelPinMixin, ChannelSlowmodeMixin, ThreadChannelMixin ]); // src/channels/PublicThreadChannel.ts var PublicThreadChannel = class extends Channel { static { __name(this, "PublicThreadChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(PublicThreadChannel, [ TextChannelMixin, ChannelOwnerMixin, ChannelParentMixin, ChannelPinMixin, ChannelSlowmodeMixin, ThreadChannelMixin, AppliedTagsMixin ]); // src/channels/StageChannel.ts var StageChannel = class extends Channel { static { __name(this, "StageChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(StageChannel, [ ChannelParentMixin, ChannelPermissionMixin, ChannelSlowmodeMixin, ChannelWebhookMixin, VoiceChannelMixin ]); // src/channels/TextChannel.ts var TextChannel = class extends Channel { static { __name(this, "TextChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(TextChannel, [ TextChannelMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelPinMixin, ChannelSlowmodeMixin, ChannelTopicMixin ]); // src/channels/VoiceChannel.ts var VoiceChannel = class extends Channel { static { __name(this, "VoiceChannel"); } constructor(data) { super(data); this.optimizeData(data); } }; Mixin(VoiceChannel, [ ChannelParentMixin, ChannelPermissionMixin, ChannelSlowmodeMixin, ChannelWebhookMixin, VoiceChannelMixin ]); // src/invites/Invite.ts import { RouteBases } from "discord-api-types/v10"; var Invite = class extends Structure { static { __name(this, "Invite"); } /** * The template used for removing data from the raw data stored for each Invite * * @remarks This template has defaults, if you want to remove additional data and keep the defaults, * use `Object.defineProperties`. To override the defaults, set this value directly. */ static DataTemplate = { set created_at(_) { }, set expires_at(_) { } }; /** * Optimized storage of {@link discord-api-types/v10#(APIActualInvite:interface).expires_at} * * @internal */ [kExpiresTimestamp] = null; /** * Optimized storage of {@link discord-api-types/v10#(APIActualInvite:interface).created_at} * * @internal */ [kCreatedTimestamp] = null; /** * @param data - The raw data received from the API for the invite */ constructor(data) { super(data); this.optimizeData(data); } /** * {@inheritDoc Structure.[kPatch]} * * @internal */ [kPatch](data) { super[kPatch](data); return this; } /** * {@inheritDoc Structure.optimizeData} * * @internal */ optimizeData(data) { if (data.expires_at) { this[kExpiresTimestamp] = Date.parse(data.expires_at); } if (data.created_at) { this[kCreatedTimestamp] = Date.parse(data.created_at); } } /** * The code for this invite */ get code() { return this[kData].code; } /** * The target type (for voice channel invites) */ get targetType() { return this[kData].target_type; } /** * The type of this invite */ get type() { return this[kData].type; } /** * The approximate number of online members of the guild this invite is for * * @remarks Only available when the invite was fetched from `GET /invites/<code>` with counts */ get approximatePresenceCount() { return this[kData].approximate_presence_count; } /** * The approximate total number of members of the guild this invite is for * * @remarks Only available when the invite was fetched from `GET /invites/<code>` with counts */ get approximateMemberCount() { return this[kData].approximate_member_count; } /** * The timestamp this invite will expire at */ get expiresTimestamp() { if (this[kExpiresTimestamp]) { return this[kExpiresTimestamp]; } const createdTimestamp = this.createdTimestamp; const maxAge = this.maxAge; if (createdTimestamp && maxAge) { this[kExpiresTimestamp] = createdTimestamp + maxAge * 1e3; } return this[kExpiresTimestamp]; } /** * The time the invite will expire at */ get expiresAt() { const expiresTimestamp = this.expiresTimestamp; return expiresTimestamp ? new Date(expiresTimestamp) : null; } /** * The number of times this invite has been used */ get uses() { return this[kData].uses; } /** * The maximum number of times this invite can be used */ get maxUses() { return this[kData].max_uses; } /** * The maximum age of the invite, in seconds, 0 for non-expiring */ get maxAge() { return this[kData].max_age; } /** * Whether this invite only grants temporary membership */ get temporary() { return this[kData].temporary; } /** * The timestamp this invite was created at */ get createdTimestamp() { return this[kCreatedTimestamp]; } /** * The time the invite was created at */ get createdAt() { const createdTimestamp = this.createdTimestamp; return createdTimestamp ? new Date(createdTimestamp) : null; } /** * The URL to the invite */ get url() { return this.code ? `${RouteBases.invite}/${this.code}` : null; } /** * When concatenated with a string, this automatically concatenates the invite's URL instead of the object. * * @returns The URL to the invite or an empty string if it doesn't have a code */ toString() { return this.url ?? ""; } /** * {@inheritDoc Structure.toJSON} */ toJSON() { const clone = super.toJSON(); if (this[kExpiresTimestamp]) { clone.expires_at = new Date(this[kExpiresTimestamp]).toISOString(); } if (this[kCreatedTimestamp]) { clone.created_at = new Date(this[kCreatedTimestamp]).toISOString(); } return clone; } /** * Returns the primitive value of the specified object. */ valueOf() { return this.code ?? super.valueOf(); } }; // src/users/AvatarDecorationData.ts var AvatarDecorationData = class extends Structure { static { __name(this, "AvatarDecorationData"); } /** * The template used for removing data from the raw data stored for each Connection */ static DataTemplate = {}; /** * @param data - The raw data received from the API for the connection */ constructor(data) { super(data); } /** * The id of the SKU this avatar decoration is part of. */ get skuId() { return this[kData].sku_id; } /** * The asset of this avatar decoration. */ get asset() { return this[kData].asset; } }; // src/users/User.ts import { DiscordSnowflake as DiscordSnowflake2 } from "@sapphire/snowflake"; var User = class extends Structure { static { __name(this, "User"); } /** * The template used for removing data from the raw data stored for each User */ static DataTemplate = {}; /** * @param data - The raw data received from the API for the user */ constructor(data) { super(data); } /** * {@inheritDoc Structure.[kPatch]} * * @internal */ [kPatch](data) { return super[kPatch](data); } /** * The user's id */ get id() { return this[kData].id; } /** * The username of the user */ get username() { return this[kData].username; } /** * The user's 4 digit tag, if a bot */ get discriminator() { return this[kData].discriminator; } /** * The user's display name, the application name for bots */ get globalName() { return this[kData].global_name; } /** * The name displayed in the client for this user when no nickname is set */ get displayName() { return this.globalName ?? this.username; } /** * The user avatar's hash */ get avatar() { return this[kData].avatar; } /** * Whether the user is a bot */ get bot() { return this[kData].bot ?? false; } /** * Whether the user is an Official Discord System user */ get system() { return this[kData].system ?? false; } /** * Whether the user has mfa enabled * * @remarks This property is only set when the user was fetched with an OAuth2 token and the `identify` scope */ get mfaEnabled() { return this[kData].mfa_enabled; } /** * The user's banner hash * * @remarks This property is only set when the user was manually fetched */ get banner() { return this[kData].banner; } /** * The base 10 accent color of the user's banner * * @remarks This property is only set when the user was manually fetched */ get accentColor() { return this[kData].accent_color; } /** * The user's primary Discord language * * @remarks This property is only set when the user was fetched with an Oauth2 token and the `identify` scope */ get locale() { return this[kData].locale; } /** * Whether the email on the user's account has been verified * * @remarks This property is only set when the user was fetched with an OAuth2 token and the `email` scope */ get verified() { return this[kData].verified; } /** * The user's email * * @remarks This property is only set when the user was fetched with an OAuth2 token and the `email` scope */ get email() { return this[kData].email; } /** * The type of nitro subscription on the user's account * * @remarks This property is only set when the user was fetched with an OAuth2 token and the `identify` scope */ get premiumType() { return this[kData].premium_type; } /** * The timestamp the user was created at */ get createdTimestamp() { return isIdSet(this.id) ? DiscordSnowflake2.timestampFrom(this.id) : null; } /** * The time the user was created at */ get createdAt() { const createdTimestamp = this.createdTimestamp; return createdTimestamp ? new Date(createdTimestamp) : null; } /** * The hexadecimal version of the user accent color, with a leading hash * * @remarks This property is only set when the user was manually fetched */ get hexAccentColor() { const accentColor = this.accentColor; if (typeof accentColor !== "number") return accentColor; return `#${accentColor.toString(16).padStart(6, "0")}`; } }; // src/users/Connection.ts var Connection = class extends Structure { static { __name(this, "Connection"); } /** * The template used for removing data from the raw data stored for each Connection */ static DataTemplate = {}; /** * @param data - The raw data received from the API for the connection */ constructor(data) { super(data); } /** * {@inheritDoc Structure.[kPatch]} * * @internal */ [kPatch](data) { return super[kPatch](data); } /** * The id of the connection account */ get id() { return this[kData].id; } /** * The username of the connection account */ get name() { return this[kData].name; } /** * The type of service this connection is for */ get type() { return this[kData].type; } /** * Whether the connection is revoked */ get revoked() { return this[kData].revoked ?? false; } /** * Whether the connection is verified */ get verified() { return this[kData].verified; } /** * Whether friend sync is enabled for this connection */ get friendSync() { return this[kData].friend_sync; } /** * Whether activities related to this connection are shown in the users presence */ get showActivity() { return this[kData].show_activity; } /** * Whether this connection has an Oauth2 token for console voice transfer */ get twoWayLink() { return this[kData].two_way_link; } /** * The visibility state for this connection */ get visibility() { return this[kData].visibility; } }; // src/utils/optimization.ts function extendTemplate(superTemplate, additions) { return Object.defineProperties(additions, Object.getOwnPropertyDescriptors(superTemplate)); } __name(extendTemplate, "extendTemplate"); export { AnnouncementChannel, AnnouncementThreadChannel, AppliedTagsMixin, AvatarDecorationData, BitField, CategoryChannel, Channel, ChannelFlagsBitField, ChannelOwnerMixin, ChannelParentMixin, ChannelPermissionMixin, ChannelPinMixin, ChannelSlowmodeMixin, ChannelTopicMixin, ChannelWebhookMixin, Connection, DMChannel, DMChannelMixin, DataTemplatePropertyName, ForumChannel, ForumTag, GroupDMChannel, GroupDMMixin, GuildChannelMixin, Invite, MediaChannel, Mixin, OptimizeDataPropertyName, PermissionOverwrite, PermissionsBitField, PrivateThreadChannel, PublicThreadChannel, StageChannel, Structure, TextChannel, TextChannelMixin, ThreadChannelMixin, ThreadMetadata, ThreadOnlyChannelMixin, User, VoiceChannel, VoiceChannelMixin, extendTemplate }; //# sourceMappingURL=index.mjs.map