@discordjs/structures
Version:
Wrapper around Discord's structures
1,884 lines (1,840 loc) • 49.6 kB
JavaScript
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