diff --git a/src/managers/ThreadMemberManager.js b/src/managers/ThreadMemberManager.js index db9e0c5..4bf78d9 100644 --- a/src/managers/ThreadMemberManager.js +++ b/src/managers/ThreadMemberManager.js @@ -1,10 +1,13 @@ 'use strict'; +const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ThreadMember = require('../structures/ThreadMember'); +let deprecationEmittedForPassingBoolean = false; + /** * Manages API methods for GuildMembers and stores their cache. * @extends {CachedManager} @@ -28,10 +31,10 @@ class ThreadMemberManager extends CachedManager { _add(data, cache = true) { const existing = this.cache.get(data.user_id); - if (cache) existing?._patch(data); + if (cache) existing?._patch(data, { cache }); if (existing) return existing; - const member = new ThreadMember(this.thread, data); + const member = new ThreadMember(this.thread, data, { cache }); if (cache) this.cache.set(data.user_id, member); return member; } @@ -110,32 +113,71 @@ class ThreadMemberManager extends CachedManager { return id; } - async _fetchOne(memberId, cache, force) { + async _fetchOne(memberId, { cache, force = false, withMember }) { if (!force) { const existing = this.cache.get(memberId); if (existing) return existing; } - const data = await this.client.api.channels(this.thread.id, 'thread-members', memberId).get(); + const data = await this.client.api.channels(this.thread.id, 'thread-members', memberId).get({ + query: { with_member: withMember }, + }); return this._add(data, cache); } - async _fetchMany(cache) { - const raw = await this.client.api.channels(this.thread.id, 'thread-members').get(); + async _fetchMany({ cache, limit, after, withMember } = {}) { + const raw = await this.client.api.channels(this.thread.id, 'thread-members').get({ + query: { with_member: withMember, limit, after }, + }); return raw.reduce((col, member) => col.set(member.user_id, this._add(member, cache)), new Collection()); } + /** + * Options used to fetch a thread member. + * @typedef {BaseFetchOptions} FetchThreadMemberOptions + * @property {boolean} [withMember] Whether to also return the guild member associated with this thread member + */ + /** + * Options used to fetch multiple thread members with guild member data. + * With `withMember` set to `true`, pagination is enabled. + * @typedef {Object} FetchThreadMembersWithGuildMemberDataOptions + * @property {true} withMember Whether to also return the guild member data + * @property {Snowflake} [after] Consider only thread members after this id + * @property {number} [limit] The maximum number of thread members to return + * @property {boolean} [cache] Whether to cache the fetched thread members and guild members + */ + + /** + * Options used to fetch multiple thread members without guild member data. + * @typedef {Object} FetchThreadMembersWithoutGuildMemberDataOptions + * @property {false} [withMember] Whether to also return the guild member data + * @property {boolean} [cache] Whether to cache the fetched thread members + */ + + /** + * Options used to fetch multiple thread members. + * @typedef {FetchThreadMembersWithGuildMemberDataOptions| + * FetchThreadMembersWithoutGuildMemberDataOptions} FetchThreadMembersOptions + */ /** * Fetches member(s) for the thread from Discord, requires access to the `GUILD_MEMBERS` gateway intent. - * @param {UserResolvable|boolean} [member] The member to fetch. If `undefined`, all members - * in the thread are fetched, and will be cached based on `options.cache`. If boolean, this serves - * the purpose of `options.cache`. - * @param {BaseFetchOptions} [options] Additional options for this fetch + * @param {UserResolvable|FetchThreadMembersOptions|boolean} [member] The member to fetch. If `undefined`, all members + * in the thread are fetched, and will be cached based on `options.cache`. + * @param {FetchThreadMemberOptions|FetchThreadMembersOptions} [options] Additional options for this fetch * @returns {Promise>} */ - fetch(member, { cache = true, force = false } = {}) { + fetch(member, options = { cache: true, force: false }) { + if (typeof member === 'boolean' && !deprecationEmittedForPassingBoolean) { + process.emitWarning( + 'Passing boolean to member option is deprecated, use cache property instead.', + 'DeprecationWarning', + ); + deprecationEmittedForPassingBoolean = true; + } const id = this.resolveId(member); - return id ? this._fetchOne(id, cache, force) : this._fetchMany(member ?? cache); + return id + ? this._fetchOne(id, options) + : this._fetchMany(typeof member !== 'boolean' ? member : { ...options, cache: member }); } } diff --git a/src/structures/ThreadMember.js b/src/structures/ThreadMember.js index 3dcf3da..abd8362 100644 --- a/src/structures/ThreadMember.js +++ b/src/structures/ThreadMember.js @@ -8,7 +8,7 @@ const ThreadMemberFlags = require('../util/ThreadMemberFlags'); * @extends {Base} */ class ThreadMember extends Base { - constructor(thread, data) { + constructor(thread, data, extra = {}) { super(thread.client); /** @@ -29,10 +29,10 @@ class ThreadMember extends Base { */ this.id = data.user_id; - this._patch(data); + this._patch(data, extra); } - _patch(data) { + _patch(data, extra = {}) { if ('join_timestamp' in data) this.joinedTimestamp = new Date(data.join_timestamp).getTime(); if ('flags' in data) { @@ -42,6 +42,17 @@ class ThreadMember extends Base { */ this.flags = new ThreadMemberFlags(data.flags).freeze(); } + + if ('member' in data) { + /** + * The guild member associated with this thread member. + * @type {?GuildMember} + * @private + */ + this.member = this.thread.guild.members._add(data.member, extra.cache); + } else { + this.member ??= null; + } } /** @@ -50,7 +61,7 @@ class ThreadMember extends Base { * @readonly */ get guildMember() { - return this.thread.guild.members.resolve(this.id); + return this.member ?? this.thread.guild.members.resolve(this.id); } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index ef7f20e..62a0b2a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3059,14 +3059,15 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhook public unpin(reason?: string): Promise; } -export class ThreadMember extends Base { - private constructor(thread: ThreadChannel, data?: RawThreadMemberData); +export class ThreadMember extends Base { + private constructor(thread: ThreadChannel, data?: RawThreadMemberData, extra?: unknown); public flags: ThreadMemberFlags; - public readonly guildMember: GuildMember | null; + public readonly guildMember: HasMemberData extends true ? GuildMember : GuildMember | null; public id: Snowflake; public readonly joinedAt: Date | null; public joinedTimestamp: number | null; public readonly manageable: boolean; + private member: If; public thread: ThreadChannel; public readonly user: User | null; public remove(reason?: string): Promise; @@ -4213,9 +4214,20 @@ export class ThreadMemberManager extends CachedManager; - public fetch(member?: UserResolvable, options?: BaseFetchOptions): Promise; + public fetch(): Promise>; + public fetch(member: ThreadMember, options?: FetchMemberOptions): Promise>; + public fetch( + member: Snowflake, + options: FetchThreadMemberOptions & { withMember: true }, + ): Promise>; + public fetch( + options: FetchThreadMembersWithGuildMemberDataOptions, + ): Promise>>; + public fetch(member: UserResolvable, options?: FetchThreadMemberOptions): Promise; + public fetch(options: FetchThreadMembersWithoutGuildMemberDataOptions): Promise>; + /** @deprecated Use `fetch(member, options)` instead. */ - public fetch(cache?: boolean): Promise>; + public fetch(cache: boolean, options?: FetchMembersOptions): Promise>; public fetchMe(options?: BaseFetchOptions): Promise; public remove(id: Snowflake | '@me', reason?: string): Promise; } @@ -5828,6 +5840,25 @@ export interface FetchReactionUsersOptions { after?: Snowflake; } +export interface FetchThreadMemberOptions extends BaseFetchOptions { + withMember?: boolean; +} +export interface FetchThreadMembersWithGuildMemberDataOptions { + withMember: true; + after?: Snowflake; + limit?: number; + cache?: boolean; +} + +export interface FetchThreadMembersWithoutGuildMemberDataOptions { + withMember: false; + cache?: boolean; +} + +export type FetchThreadMembersOptions = + | FetchThreadMembersWithGuildMemberDataOptions + | FetchThreadMembersWithoutGuildMemberDataOptions; + export interface FetchThreadsOptions { archived?: FetchArchivedThreadOptions; active?: boolean;