Add Guild-related structures and additional bitfields

Co-authored-by: iCrawl <20760160+iCrawl@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-09 22:14:32 +00:00
parent f3355cfaa1
commit e0ba4f1836
16 changed files with 21969 additions and 0 deletions

21061
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
import { GuildMemberFlags } from 'discord-api-types/v10';
import { BitField } from './BitField.js';
/**
* Data structure that makes it easy to interact with a {@link GuildMember#flags} bitfield.
*/
export class GuildMemberFlagsBitField extends BitField<keyof typeof GuildMemberFlags> {
/**
* Numeric guild member flags.
*/
public static override readonly Flags = GuildMemberFlags;
public override toJSON() {
return super.toJSON(true);
}
}

View File

@@ -0,0 +1,16 @@
import { RoleFlags } from 'discord-api-types/v10';
import { BitField } from './BitField.js';
/**
* Data structure that makes it easy to interact with a {@link Role#flags} bitfield.
*/
export class RoleFlagsBitField extends BitField<keyof typeof RoleFlags> {
/**
* Numeric role flags.
*/
public static override readonly Flags = RoleFlags;
public override toJSON() {
return super.toJSON(true);
}
}

View File

@@ -0,0 +1,16 @@
import { GuildSystemChannelFlags } from 'discord-api-types/v10';
import { BitField } from './BitField.js';
/**
* Data structure that makes it easy to interact with a {@link Guild#systemChannelFlags} bitfield.
*/
export class SystemChannelFlagsBitField extends BitField<keyof typeof GuildSystemChannelFlags> {
/**
* Numeric system channel flags.
*/
public static override readonly Flags = GuildSystemChannelFlags;
public override toJSON() {
return super.toJSON(true);
}
}

View File

@@ -0,0 +1,16 @@
import { UserFlags } from 'discord-api-types/v10';
import { BitField } from './BitField.js';
/**
* Data structure that makes it easy to interact with a {@link User#flags} bitfield.
*/
export class UserFlagsBitField extends BitField<keyof typeof UserFlags> {
/**
* Numeric user flags.
*/
public static override readonly Flags = UserFlags;
public override toJSON() {
return super.toJSON(true);
}
}

View File

@@ -2,5 +2,9 @@ export * from './BitField.js';
export * from './AttachmentFlagsBitField.js';
export * from './ChannelFlagsBitField.js';
export * from './GuildMemberFlagsBitField.js';
export * from './MessageFlagsBitField.js';
export * from './PermissionsBitField.js';
export * from './RoleFlagsBitField.js';
export * from './SystemChannelFlagsBitField.js';
export * from './UserFlagsBitField.js';

View File

@@ -0,0 +1,269 @@
import type { APIGuild } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a guild (server) on Discord.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has substructures `WelcomeScreen`, `Role`, `GuildEmoji`, `Sticker`, which need to be instantiated and stored by an extending class using it
*/
export abstract class Guild<Omitted extends keyof APIGuild | '' = ''> extends Structure<APIGuild, Omitted> {
/**
* The template used for removing data from the raw data stored for each Guild
*/
public static override readonly DataTemplate: Partial<APIGuild> = {};
/**
* @param data - The raw data received from the API for the guild
*/
public constructor(data: Partialize<APIGuild, Omitted>) {
super(data);
}
/**
* The guild id
*/
public get id() {
return this[kData].id;
}
/**
* The guild name
*/
public get name() {
return this[kData].name;
}
/**
* The guild icon hash
*/
public get icon() {
return this[kData].icon;
}
/**
* The guild splash hash
*/
public get splash() {
return this[kData].splash;
}
/**
* The guild discovery splash hash
*/
public get discoverySplash() {
return this[kData].discovery_splash;
}
/**
* The id of the owner
*/
public get ownerId() {
return this[kData].owner_id;
}
/**
* The id of the afk channel
*/
public get afkChannelId() {
return this[kData].afk_channel_id;
}
/**
* Afk timeout in seconds
*/
public get afkTimeout() {
return this[kData].afk_timeout;
}
/**
* Whether the guild widget is enabled
*/
public get widgetEnabled() {
return this[kData].widget_enabled;
}
/**
* The channel id that the widget will generate an invite to
*/
public get widgetChannelId() {
return this[kData].widget_channel_id;
}
/**
* Verification level required for the guild
*/
public get verificationLevel() {
return this[kData].verification_level;
}
/**
* Default message notifications level
*/
public get defaultMessageNotifications() {
return this[kData].default_message_notifications;
}
/**
* Explicit content filter level
*/
public get explicitContentFilter() {
return this[kData].explicit_content_filter;
}
/**
* Enabled guild features
*/
public get features() {
return this[kData].features;
}
/**
* Required MFA level for the guild
*/
public get mfaLevel() {
return this[kData].mfa_level;
}
/**
* Application id of the guild creator if it is bot-created
*/
public get applicationId() {
return this[kData].application_id;
}
/**
* The id of the channel where guild notices such as welcome messages and boost events are posted
*/
public get systemChannelId() {
return this[kData].system_channel_id;
}
/**
* System channel flags
*/
public get systemChannelFlags() {
return this[kData].system_channel_flags;
}
/**
* The id of the channel where Community guilds can display rules and/or guidelines
*/
public get rulesChannelId() {
return this[kData].rules_channel_id;
}
/**
* The maximum number of presences for the guild
*/
public get maxPresences() {
return this[kData].max_presences;
}
/**
* The maximum number of members for the guild
*/
public get maxMembers() {
return this[kData].max_members;
}
/**
* The vanity url code for the guild
*/
public get vanityUrlCode() {
return this[kData].vanity_url_code;
}
/**
* The description of the guild
*/
public get description() {
return this[kData].description;
}
/**
* The guild banner hash
*/
public get banner() {
return this[kData].banner;
}
/**
* Premium tier (Server Boost level)
*/
public get premiumTier() {
return this[kData].premium_tier;
}
/**
* The number of boosts this guild currently has
*/
public get premiumSubscriptionCount() {
return this[kData].premium_subscription_count;
}
/**
* The preferred locale of a Community guild
*/
public get preferredLocale() {
return this[kData].preferred_locale;
}
/**
* The id of the channel where admins and moderators of Community guilds receive notices from Discord
*/
public get publicUpdatesChannelId() {
return this[kData].public_updates_channel_id;
}
/**
* The maximum amount of users in a video channel
*/
public get maxVideoChannelUsers() {
return this[kData].max_video_channel_users;
}
/**
* The maximum amount of users in a stage video channel
*/
public get maxStageVideoChannelUsers() {
return this[kData].max_stage_video_channel_users;
}
/**
* Approximate number of members in this guild
*/
public get approximateMemberCount() {
return this[kData].approximate_member_count;
}
/**
* Approximate number of non-offline members in this guild
*/
public get approximatePresenceCount() {
return this[kData].approximate_presence_count;
}
/**
* The NSFW level of the guild
*/
public get nsfwLevel() {
return this[kData].nsfw_level;
}
/**
* Whether the guild has the boost progress bar enabled
*/
public get premiumProgressBarEnabled() {
return this[kData].premium_progress_bar_enabled;
}
/**
* The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord
*/
public get safetyAlertsChannelId() {
return this[kData].safety_alerts_channel_id;
}
}

View File

@@ -0,0 +1,31 @@
import type { APIBan } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a ban on a guild.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has a substructure `User`, which needs to be instantiated and stored by an extending class using it
*/
export abstract class GuildBan<Omitted extends keyof APIBan | '' = ''> extends Structure<APIBan, Omitted> {
/**
* The template used for removing data from the raw data stored for each GuildBan
*/
public static override readonly DataTemplate: Partial<APIBan> = {};
/**
* @param data - The raw data received from the API for the guild ban
*/
public constructor(data: Partialize<APIBan, Omitted>) {
super(data);
}
/**
* The reason for the ban
*/
public get reason() {
return this[kData].reason;
}
}

View File

@@ -0,0 +1,73 @@
import type { APIEmoji } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a guild emoji.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has a substructure `User`, which needs to be instantiated and stored by an extending class using it
*/
export abstract class GuildEmoji<Omitted extends keyof APIEmoji | '' = ''> extends Structure<APIEmoji, Omitted> {
/**
* The template used for removing data from the raw data stored for each GuildEmoji
*/
public static override readonly DataTemplate: Partial<APIEmoji> = {};
/**
* @param data - The raw data received from the API for the guild emoji
*/
public constructor(data: Partialize<APIEmoji, Omitted>) {
super(data);
}
/**
* The emoji id
*/
public get id() {
return this[kData].id;
}
/**
* The emoji name
*/
public get name() {
return this[kData].name;
}
/**
* Roles allowed to use this emoji
*/
public get roles() {
return this[kData].roles;
}
/**
* Whether this emoji must be wrapped in colons
*/
public get requireColons() {
return this[kData].require_colons;
}
/**
* Whether this emoji is managed
*/
public get managed() {
return this[kData].managed;
}
/**
* Whether this emoji is animated
*/
public get animated() {
return this[kData].animated;
}
/**
* Whether this emoji can be used, may be false due to loss of Server Boosts
*/
public get available() {
return this[kData].available;
}
}

View File

@@ -0,0 +1,196 @@
import type { APIGuildMember } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
type GuildMemberOmittedKeys = 'communication_disabled_until' | 'joined_at' | 'premium_since';
/**
* Represents a member of a guild.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has substructures `User` and `AvatarDecorationData`, which need to be instantiated and stored by an extending class using it
*/
export abstract class GuildMember<Omitted extends keyof APIGuildMember | '' = ''> extends Structure<
APIGuildMember,
GuildMemberOmittedKeys | Omitted
> {
/**
* The template used for removing data from the raw data stored for each GuildMember
*/
public static override readonly DataTemplate: Partial<APIGuildMember> = {
// Timestamps are optimized to numbers
set joined_at(_: string | null) {},
set premium_since(_: string | null) {},
set communication_disabled_until(_: string | null) {},
};
/**
* @param data - The raw data received from the API for the guild member
*/
public constructor(data: Partialize<APIGuildMember, GuildMemberOmittedKeys | Omitted>) {
super(data as Readonly<Partial<APIGuildMember>>);
this.optimizeData(data as Partial<APIGuildMember>);
}
/**
* The user id of this member
*/
public get userId() {
return (this[kData] as { user?: { id: string } }).user?.id;
}
/**
* The guild nickname of this member
*/
public get nick() {
return this[kData].nick;
}
/**
* The member's guild avatar hash
*/
public get avatar() {
return this[kData].avatar;
}
/**
* Array of role ids
*/
public get roles() {
return this[kData].roles;
}
/**
* Timestamp the member joined the guild
*/
public get joinedTimestamp(): number | null {
return (this[kData] as unknown as { joined_at_timestamp?: number }).joined_at_timestamp ?? null;
}
/**
* When the member joined the guild
*/
public get joinedAt() {
const joinedTimestamp = this.joinedTimestamp;
return joinedTimestamp ? new Date(joinedTimestamp) : null;
}
/**
* Timestamp of when the member started boosting the guild
*/
public get premiumSinceTimestamp(): number | null {
return (this[kData] as unknown as { premium_since_timestamp?: number }).premium_since_timestamp ?? null;
}
/**
* When the member started boosting the guild
*/
public get premiumSince() {
const premiumSinceTimestamp = this.premiumSinceTimestamp;
return premiumSinceTimestamp ? new Date(premiumSinceTimestamp) : null;
}
/**
* Whether the member is deafened in voice channels
*/
public get deaf() {
return this[kData].deaf;
}
/**
* Whether the member is muted in voice channels
*/
public get mute() {
return this[kData].mute;
}
/**
* Guild member flags
*/
public get flags() {
return this[kData].flags;
}
/**
* Whether the member has not yet passed the guild's Membership Screening requirements
*/
public get pending() {
return this[kData].pending;
}
/**
* Total permissions of the member in the channel, including overwrites
*/
public get permissions() {
return (this[kData] as { permissions?: string }).permissions;
}
/**
* Timestamp of when the time out will be removed
*/
public get communicationDisabledUntilTimestamp(): number | null {
return (
(this[kData] as unknown as { communication_disabled_until_timestamp?: number })
.communication_disabled_until_timestamp ?? null
);
}
/**
* When the timeout will be removed
*/
public get communicationDisabledUntil() {
const timestamp = this.communicationDisabledUntilTimestamp;
return timestamp ? new Date(timestamp) : null;
}
/**
* Whether this member is currently timed out
*/
public get isCommunicationDisabled() {
const timestamp = this.communicationDisabledUntilTimestamp;
return timestamp !== null && timestamp > Date.now();
}
protected override optimizeData(data: Partial<APIGuildMember>) {
if (data.joined_at !== undefined && data.joined_at !== null) {
(this[kData] as unknown as { joined_at_timestamp: number }).joined_at_timestamp = new Date(
data.joined_at,
).getTime();
}
if (data.premium_since !== undefined && data.premium_since !== null) {
(this[kData] as unknown as { premium_since_timestamp: number }).premium_since_timestamp = new Date(
data.premium_since,
).getTime();
}
if (data.communication_disabled_until !== undefined && data.communication_disabled_until !== null) {
(
this[kData] as unknown as { communication_disabled_until_timestamp: number }
).communication_disabled_until_timestamp = new Date(data.communication_disabled_until).getTime();
}
}
public override toJSON(): APIGuildMember {
const data = { ...this[kData] } as APIGuildMember;
const joinedTimestamp = this.joinedTimestamp;
if (joinedTimestamp !== null) {
data.joined_at = dateToDiscordISOTimestamp(new Date(joinedTimestamp));
}
const premiumSinceTimestamp = this.premiumSinceTimestamp;
if (premiumSinceTimestamp !== null) {
data.premium_since = dateToDiscordISOTimestamp(new Date(premiumSinceTimestamp));
}
const communicationDisabledUntilTimestamp = this.communicationDisabledUntilTimestamp;
if (communicationDisabledUntilTimestamp !== null) {
data.communication_disabled_until = dateToDiscordISOTimestamp(new Date(communicationDisabledUntilTimestamp));
}
return data;
}
}

View File

@@ -0,0 +1,109 @@
import type { APIRole } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a role in a guild.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has a substructure `RoleTags`, which needs to be instantiated and stored by an extending class using it
*/
export abstract class Role<Omitted extends keyof APIRole | '' = ''> extends Structure<APIRole, Omitted> {
/**
* The template used for removing data from the raw data stored for each Role
*/
public static override readonly DataTemplate: Partial<APIRole> = {};
/**
* @param data - The raw data received from the API for the role
*/
public constructor(data: Partialize<APIRole, Omitted>) {
super(data);
}
/**
* The role id
*/
public get id() {
return this[kData].id;
}
/**
* The role name
*/
public get name() {
return this[kData].name;
}
/**
* Integer representation of hexadecimal color code
*/
public get color() {
return this[kData].color;
}
/**
* Whether this role is pinned in the user listing
*/
public get hoist() {
return this[kData].hoist;
}
/**
* The role icon hash
*/
public get icon() {
return this[kData].icon;
}
/**
* The role unicode emoji
*/
public get unicodeEmoji() {
return this[kData].unicode_emoji;
}
/**
* Position of this role
*/
public get position() {
return this[kData].position;
}
/**
* Permission bit set
*/
public get permissions() {
return this[kData].permissions;
}
/**
* Whether this role is managed by an integration
*/
public get managed() {
return this[kData].managed;
}
/**
* Whether this role is mentionable
*/
public get mentionable() {
return this[kData].mentionable;
}
/**
* Role flags combined as a bitfield
*/
public get flags() {
return this[kData].flags;
}
/**
* The hexadecimal version of the role color, with a leading hash
*/
public get hexColor() {
const color = this.color as number;
return `#${color.toString(16).padStart(6, '0')}`;
}
}

View File

@@ -0,0 +1,65 @@
import type { APIRoleTags } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents tags for a role.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
*/
export class RoleTags<Omitted extends keyof APIRoleTags | '' = ''> extends Structure<APIRoleTags, Omitted> {
/**
* The template used for removing data from the raw data stored for each RoleTags
*/
public static override readonly DataTemplate: Partial<APIRoleTags> = {};
/**
* @param data - The raw data received from the API for the role tags
*/
public constructor(data: Partialize<APIRoleTags, Omitted>) {
super(data);
}
/**
* The id of the bot this role belongs to
*/
public get botId() {
return this[kData].bot_id;
}
/**
* The id of the integration this role belongs to
*/
public get integrationId() {
return this[kData].integration_id;
}
/**
* Whether this is the guild's premium subscriber role
*/
public get premiumSubscriber() {
return this[kData].premium_subscriber !== undefined;
}
/**
* The id of this role's subscription sku and listing
*/
public get subscriptionListingId() {
return this[kData].subscription_listing_id;
}
/**
* Whether this role is available for purchase
*/
public get availableForPurchase() {
return this[kData].available_for_purchase !== undefined;
}
/**
* Whether this role is a guild's linked role
*/
public get guildConnections() {
return this[kData].guild_connections !== undefined;
}
}

View File

@@ -0,0 +1,54 @@
import type { APIGuildWelcomeScreenChannel } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a channel in a guild's welcome screen.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
*/
export class WelcomeChannel<Omitted extends keyof APIGuildWelcomeScreenChannel | '' = ''> extends Structure<
APIGuildWelcomeScreenChannel,
Omitted
> {
/**
* The template used for removing data from the raw data stored for each WelcomeChannel
*/
public static override readonly DataTemplate: Partial<APIGuildWelcomeScreenChannel> = {};
/**
* @param data - The raw data received from the API for the welcome channel
*/
public constructor(data: Partialize<APIGuildWelcomeScreenChannel, Omitted>) {
super(data);
}
/**
* The channel's id
*/
public get channelId() {
return this[kData].channel_id;
}
/**
* The description shown for the channel
*/
public get description() {
return this[kData].description;
}
/**
* The emoji id, if the emoji is custom
*/
public get emojiId() {
return this[kData].emoji_id;
}
/**
* The emoji name if custom, the unicode character if standard, or null if no emoji is set
*/
public get emojiName() {
return this[kData].emoji_name;
}
}

View File

@@ -0,0 +1,34 @@
import type { APIGuildWelcomeScreen } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { kData } from '../utils/symbols.js';
import type { Partialize } from '../utils/types.js';
/**
* Represents a guild's welcome screen.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has a substructure `WelcomeChannel`, which needs to be instantiated and stored by an extending class using it
*/
export abstract class WelcomeScreen<Omitted extends keyof APIGuildWelcomeScreen | '' = ''> extends Structure<
APIGuildWelcomeScreen,
Omitted
> {
/**
* The template used for removing data from the raw data stored for each WelcomeScreen
*/
public static override readonly DataTemplate: Partial<APIGuildWelcomeScreen> = {};
/**
* @param data - The raw data received from the API for the welcome screen
*/
public constructor(data: Partialize<APIGuildWelcomeScreen, Omitted>) {
super(data);
}
/**
* The server description shown in the welcome screen
*/
public get description() {
return this[kData].description;
}
}

View File

@@ -0,0 +1,8 @@
export * from './Guild.js';
export * from './GuildBan.js';
export * from './GuildEmoji.js';
export * from './GuildMember.js';
export * from './Role.js';
export * from './RoleTags.js';
export * from './WelcomeChannel.js';
export * from './WelcomeScreen.js';

View File

@@ -1,5 +1,6 @@
export * from './bitfields/index.js';
export * from './channels/index.js';
export * from './guilds/index.js';
export * from './interactions/index.js';
export * from './invites/index.js';
export * from './messages/index.js';