diff --git a/src/index.js b/src/index.js index 1842a8421..8425c93b8 100644 --- a/src/index.js +++ b/src/index.js @@ -18,6 +18,7 @@ exports.Constants = require('./util/Constants'); exports.DataResolver = require('./util/DataResolver'); exports.DiscordAPIError = require('./rest/DiscordAPIError'); exports.Formatters = require('./util/Formatters'); +exports.GuildMemberFlags = require('./util/GuildMemberFlags'); exports.HTTPError = require('./rest/HTTPError'); exports.Intents = require('./util/Intents'); exports.LimitedCollection = require('./util/LimitedCollection'); diff --git a/src/managers/GuildMemberManager.js b/src/managers/GuildMemberManager.js index 110bd8591..295ab917e 100644 --- a/src/managers/GuildMemberManager.js +++ b/src/managers/GuildMemberManager.js @@ -10,6 +10,7 @@ const { GuildMember } = require('../structures/GuildMember'); const { Role } = require('../structures/Role'); const { Events, Opcodes } = require('../util/Constants'); const { PartialTypes } = require('../util/Constants'); +const GuildMemberFlags = require('../util/GuildMemberFlags'); const SnowflakeUtil = require('../util/SnowflakeUtil'); /** @@ -260,6 +261,7 @@ class GuildMemberManager extends CachedManager { * (if they are connected to voice), or `null` if you want to disconnect them from voice * @property {DateResolvable|null} [communicationDisabledUntil] The date or timestamp * for the member's communication to be disabled until. Provide `null` to enable communication again. + * @property {GuildMemberFlagsResolvable} [flags] The flags to set for the member */ /** @@ -292,6 +294,8 @@ class GuildMemberManager extends CachedManager { _data.communication_disabled_until = _data.communicationDisabledUntil && new Date(_data.communicationDisabledUntil).toISOString(); + _data.flags = _data.flags && GuildMemberFlags.resolve(_data.flags); + let endpoint = this.client.api.guilds(this.guild.id); if (id === this.client.user.id) { const keys = Object.keys(data); diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index b568f44b4..28e7770cd 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -6,6 +6,7 @@ const VoiceState = require('./VoiceState'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const { Error } = require('../errors'); const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); +const GuildMemberFlags = require('../util/GuildMemberFlags'); const Permissions = require('../util/Permissions'); /** @@ -95,6 +96,15 @@ class GuildMember extends Base { this.communicationDisabledUntilTimestamp = data.communication_disabled_until && Date.parse(data.communication_disabled_until); } + if ('flags' in data) { + /** + * The flags of this member + * @type {Readonly} + */ + this.flags = new GuildMemberFlags(data.flags).freeze(); + } else { + this.flags ??= new GuildMemberFlags().freeze(); + } } _clone() { @@ -347,6 +357,16 @@ class GuildMember extends Base { return this.edit({ nick }, reason); } + /** + * Sets the flags for this member. + * @param {GuildMemberFlagsResolvable} flags The flags to set + * @param {string} [reason] Reason for setting the flags + * @returns {Promise} + */ + setFlags(flags, reason) { + return this.edit({ flags, reason }); + } + /** * Creates a DM channel between the client and this member. * @param {boolean} [force=false] Whether to skip the cache check and request the API @@ -446,6 +466,7 @@ class GuildMember extends Base { this.avatar === member.avatar && this.pending === member.pending && this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp && + this.flags.equals(member.flags) && (this._roles === member._roles || (this._roles.length === member._roles.length && this._roles.every((role, i) => role === member._roles[i]))) ); diff --git a/src/util/GuildMemberFlags.js b/src/util/GuildMemberFlags.js new file mode 100644 index 000000000..b4c3709cd --- /dev/null +++ b/src/util/GuildMemberFlags.js @@ -0,0 +1,43 @@ +'use strict'; + +const BitField = require('./BitField'); + +/** + * Data structure that makes it easy to interact with an {@link GuildMember#flags} bitfield. + * @extends {BitField} + */ +class GuildMemberFlags extends BitField {} + +/** + * @name GuildMemberFlags + * @kind constructor + * @memberof GuildMemberFlags + * @param {BitFieldResolvable} [bits=0] Bit(s) to read from + */ + +/** + * Numeric guild member flags. All available properties: + * * `DID_REJOIN` + * * `COMPLETED_ONBOARDING` + * * `BYPASSES_VERIFICATION` + * * `STARTED_ONBOARDING` + * @type {Object} + * @see {@link https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags} + */ +GuildMemberFlags.FLAGS = { + DID_REJOIN: 1 << 0, + COMPLETED_ONBOARDING: 1 << 1, + BYPASSES_VERIFICATION: 1 << 2, + STARTED_ONBOARDING: 1 << 3, +}; + +/** + * Data that can be resolved to give a guild member flag bitfield. This can be: + * * A string (see {@link GuildMemberFlags.FLAGS}) + * * A guild member flag + * * An instance of GuildMemberFlags + * * An Array of GuildMemberFlagsResolvable + * @typedef {string|number|GuildMemberFlags|GuildMemberFlagsResolvable[]} GuildMemberFlagsResolvable + */ + +module.exports = GuildMemberFlags; diff --git a/typings/index.d.ts b/typings/index.d.ts index 63a0067bf..b5efb6b13 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1176,6 +1176,7 @@ export class GuildMember extends PartialTextBasedChannel(Base) { public readonly displayColor: number; public readonly displayHexColor: HexColorString; public readonly displayName: string; + public flags: Readonly; public guild: Guild; public readonly id: Snowflake; public pending: boolean; @@ -1211,11 +1212,17 @@ export class GuildMember extends PartialTextBasedChannel(Base) { public kick(reason?: string): Promise; public permissionsIn(channel: GuildChannelResolvable): Readonly; public setNickname(nickname: string | null, reason?: string): Promise; + public setFlags(flags: GuildMemberFlagsResolvable): Promise; public toJSON(): unknown; public toString(): MemberMention; public valueOf(): string; } +export class GuildMemberFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; +} + export class GuildPreview extends Base { private constructor(client: Client, data: RawGuildPreviewData); public approximateMemberCount: number; @@ -5356,8 +5363,13 @@ export interface GuildMemberEditData { deaf?: boolean; channel?: GuildVoiceChannelResolvable | null; communicationDisabledUntil?: DateResolvable | null; + flags?: GuildMemberFlagsResolvable; } +export type GuildMemberFlagsString = 'DID_REJOIN' | 'COMPLETED_ONBOARDING'| 'BYPASSES_VERIFICATION'| 'STARTED_ONBOARDING'; + +export type GuildMemberFlagsResolvable = BitFieldResolvable; + export type GuildMemberResolvable = GuildMember | UserResolvable; export type GuildResolvable = Guild | NonThreadGuildBasedChannel | GuildMember | GuildEmoji | Invite | Role | Snowflake;