From 0f97dc03bd9b0be69e271fe395133862e73aec5f Mon Sep 17 00:00:00 2001
From: Elysia <71698422+aiko-chan-ai@users.noreply.github.com>
Date: Sun, 5 Feb 2023 15:14:42 +0700
Subject: [PATCH] feat v13: Support pagination for fetching thread members
#9045 djs
---
src/managers/ThreadMemberManager.js | 66 +++++++++++++++++++++++------
src/structures/ThreadMember.js | 19 +++++++--
typings/index.d.ts | 41 +++++++++++++++---
3 files changed, 105 insertions(+), 21 deletions(-)
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;