From e9a8eb323f3a554dc8f9dab361cd1bac7b88e1cc Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:24:09 +0000 Subject: [PATCH] fix(ThreadManager): add `members` and conditionally include `hasMore` (#9164) * fix(ThreadManager): conditionally include `hasMore` * types: fix tests --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/managers/GuildChannelManager.js | 9 ++++- .../discord.js/src/managers/ThreadManager.js | 38 ++++++++++--------- packages/discord.js/typings/index.d.ts | 12 +++++- packages/discord.js/typings/index.test-d.ts | 15 ++++++++ 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index a5fa1602e..1bf01fadd 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -451,7 +451,14 @@ class GuildChannelManager extends CachedManager { } /** - * Obtains all active thread channels in the guild from Discord + * Data returned from fetching threads. + * @typedef {Object} FetchedThreads + * @property {Collection} threads The threads that were fetched + * @property {Collection} members The thread members in the received threads + */ + + /** + * Obtains all active thread channels in the guild. * @param {boolean} [cache=true] Whether to cache the fetched data * @returns {Promise} * @example diff --git a/packages/discord.js/src/managers/ThreadManager.js b/packages/discord.js/src/managers/ThreadManager.js index 5ff657d16..bb0f0957d 100644 --- a/packages/discord.js/src/managers/ThreadManager.js +++ b/packages/discord.js/src/managers/ThreadManager.js @@ -86,7 +86,8 @@ class ThreadManager extends CachedManager { * ThreadChannelResolvable then the specified thread will be fetched. Fetches all active threads if `undefined` * @param {BaseFetchOptions} [cacheOptions] Additional options for this fetch. The `force` field gets ignored * if `options` is not a {@link ThreadChannelResolvable} - * @returns {Promise} + * @returns {Promise} + * {@link FetchedThreads} if active & {@link FetchedThreadsMore} if archived. * @example * // Fetch a thread by its id * channel.threads.fetch('831955138126104859') @@ -124,9 +125,8 @@ class ThreadManager extends CachedManager { */ /** - * The data returned from a thread fetch that returns multiple threads. - * @typedef {Object} FetchedThreads - * @property {Collection} threads The threads that were fetched, with any members returned + * Data returned from fetching multiple threads. + * @typedef {FetchedThreads} FetchedThreadsMore * @property {?boolean} hasMore Whether there are potentially additional threads that require a subsequent call */ @@ -136,7 +136,7 @@ class ThreadManager extends CachedManager { * in the parent channel. * @param {FetchArchivedThreadOptions} [options] The options to fetch archived threads * @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already - * @returns {Promise} + * @returns {Promise} */ async fetchArchived({ type = 'public', fetchAll = false, before, limit } = {}, cache = true) { let path = Routes.channelThreads(this.channel.id, type); @@ -171,15 +171,13 @@ class ThreadManager extends CachedManager { } /** - * Obtains the accessible active threads from Discord. - * This method requires the {@link PermissionFlagsBits.ReadMessageHistory} permission - * in the parent channel. - * @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already + * Obtains all active thread channels in the guild. + * This internally calls {@link GuildChannelManager#fetchActiveThreads}. + * @param {boolean} [cache=true] Whether to cache the fetched data * @returns {Promise} */ - async fetchActive(cache = true) { - const raw = await this.client.rest.get(Routes.guildActiveThreads(this.channel.guild.id)); - return this.constructor._mapThreads(raw, this.client, { parent: this.channel, cache }); + fetchActive(cache = true) { + return this.channel.guild.channels.fetchActiveThreads(cache); } static _mapThreads(rawThreads, client, { parent, guild, cache }) { @@ -188,12 +186,18 @@ class ThreadManager extends CachedManager { if (parent && thread.parentId !== parent.id) return coll; return coll.set(thread.id, thread); }, new Collection()); + // Discord sends the thread id as id in this object - for (const rawMember of rawThreads.members) client.channels.cache.get(rawMember.id)?.members._add(rawMember); - return { - threads, - hasMore: rawThreads.has_more ?? false, - }; + const threadMembers = rawThreads.members.reduce( + (coll, raw) => coll.set(raw.user_id, threads.get(raw.id).members._add(raw)), + new Collection(), + ); + + const response = { threads, members: threadMembers }; + + // The GET `/guilds/{guild.id}/threads/active` route does not return `has_more`. + if ('has_more' in rawThreads) response.hasMore = rawThreads.has_more; + return response; } } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index cb80ea701..46257ab81 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -4109,8 +4109,12 @@ export class ThreadManager extends CachedManage protected constructor(channel: TextChannel | NewsChannel | ForumChannel, iterable?: Iterable); public channel: If; public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise; + public fetch( + options: FetchThreadsOptions & { archived: FetchArchivedThreadOptions }, + cacheOptions?: { cache?: boolean }, + ): Promise; public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise; - public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise; + public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise; public fetchActive(cache?: boolean): Promise; } @@ -5148,7 +5152,11 @@ export interface FetchChannelOptions extends BaseFetchOptions { export interface FetchedThreads { threads: Collection; - hasMore?: boolean; + members: Collection; +} + +export interface FetchedThreadsMore extends FetchedThreads { + hasMore: boolean; } export interface FetchGuildOptions extends BaseFetchOptions { diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index a352a83f1..69b5e7c86 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -160,6 +160,9 @@ import { PublicThreadChannel, GuildMemberManager, GuildMemberFlagsBitField, + ThreadManager, + FetchedThreads, + FetchedThreadsMore, } from '.'; import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd'; import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders'; @@ -1479,6 +1482,18 @@ declare const guildChannelManager: GuildChannelManager; expectType(message.mentions.members); } +declare const threadManager: ThreadManager; +{ + expectType>(threadManager.fetch('12345678901234567')); + expectType>(threadManager.fetch('12345678901234567', { cache: true, force: false })); + expectType>(threadManager.fetch()); + expectType>(threadManager.fetch({})); + expectType>(threadManager.fetch({ archived: { limit: 4 } })); + + // @ts-expect-error The force option has no effect here. + threadManager.fetch({ archived: {} }, { force: true }); +} + declare const guildForumThreadManager: GuildForumThreadManager; expectType(guildForumThreadManager.channel);