diff --git a/packages/discord.js/src/managers/MessageManager.js b/packages/discord.js/src/managers/MessageManager.js index 284e15cf8545..30a4bb42dbe8 100644 --- a/packages/discord.js/src/managers/MessageManager.js +++ b/packages/discord.js/src/managers/MessageManager.js @@ -123,24 +123,63 @@ class MessageManager extends CachedManager { return data.reduce((_data, message) => _data.set(message.id, this._add(message, options.cache)), new Collection()); } + /** + * Options used to fetch pinned messages. + * + * @typedef {Object} FetchPinnedMessagesOptions + * @property {DateResolvable} [before] Consider only pinned messages before this time + * @property {number} [limit] The maximum number of pinned messages to return + * @property {boolean} [cache] Whether to cache the pinned messages + */ + + /** + * Data returned from fetching pinned messages. + * + * @typedef {Object} FetchPinnedMessagesResponse + * @property {MessagePin[]} items The pinned messages + * @property {boolean} hasMore Whether there are additional pinned messages that require a subsequent call + */ + + /** + * Pinned message data returned from fetching pinned messages. + * + * @typedef {Object} MessagePin + * @property {Date} pinnedAt The time the message was pinned at + * @property {number} pinnedTimestamp The timestamp the message was pinned at + * @property {Message} message The pinned message + */ + /** * Fetches the pinned messages of this channel and returns a collection of them. * The returned Collection does not contain any reaction data of the messages. * Those need to be fetched separately. * - * @param {boolean} [cache=true] Whether to cache the message(s) - * @returns {Promise>} + * @param {FetchPinnedMessagesOptions} [options={}] Options for fetching pinned messages + * @returns {Promise} * @example * // Get pinned messages - * channel.messages.fetchPinned() - * .then(messages => console.log(`Received ${messages.size} messages`)) + * channel.messages.fetchPins() + * .then(messages => console.log(`Received ${messages.items.length} messages`)) * .catch(console.error); */ - async fetchPinned(cache = true) { - const data = await this.client.rest.get(Routes.channelPins(this.channel.id)); - const messages = new Collection(); - for (const message of data) messages.set(message.id, this._add(message, cache)); - return messages; + async fetchPins(options = {}) { + const data = await this.client.rest.get(Routes.channelMessagesPins(this.channel.id), { + query: makeURLSearchParams({ + ...options, + before: options.before && new Date(options.before).toISOString(), + }), + }); + + return { + items: data.items.map(item => ({ + pinnedTimestamp: Date.parse(item.pinned_at), + get pinnedAt() { + return new Date(this.pinnedTimestamp); + }, + message: this._add(item.message, options.cache), + })), + hasMore: data.has_more, + }; } /** @@ -221,7 +260,7 @@ class MessageManager extends CachedManager { const messageId = this.resolveId(message); if (!messageId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); - await this.client.rest.put(Routes.channelPin(this.channel.id, messageId), { reason }); + await this.client.rest.put(Routes.channelMessagesPin(this.channel.id, messageId), { reason }); } /** @@ -235,7 +274,7 @@ class MessageManager extends CachedManager { const messageId = this.resolveId(message); if (!messageId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); - await this.client.rest.delete(Routes.channelPin(this.channel.id, messageId), { reason }); + await this.client.rest.delete(Routes.channelMessagesPin(this.channel.id, messageId), { reason }); } /** diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index c39dd4db68c2..53e83f95278d 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -4516,7 +4516,7 @@ export abstract class MessageManager extends ): Promise>; public fetch(options: FetchMessageOptions | MessageResolvable): Promise>; public fetch(options?: FetchMessagesOptions): Promise>>; - public fetchPinned(cache?: boolean): Promise>>; + public fetchPins(options?: FetchPinnedMessagesOptions): Promise>; public react(message: MessageResolvable, emoji: EmojiIdentifierResolvable): Promise; public pin(message: MessageResolvable, reason?: string): Promise; public unpin(message: MessageResolvable, reason?: string): Promise; @@ -5800,6 +5800,23 @@ export interface FetchMessagesOptions { limit?: number; } +export interface FetchPinnedMessagesOptions { + before?: DateResolvable; + cache?: boolean; + limit?: number; +} + +export interface FetchPinnedMessagesResponse { + hasMore: boolean; + items: readonly MessagePin[]; +} + +export interface MessagePin { + message: Message; + get pinnedAt(): Date; + pinnedTimestamp: number; +} + export interface FetchReactionUsersOptions { after?: Snowflake; limit?: number; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 0ec7fe3bb9e6..7f2ea9e9d3ef 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -99,6 +99,7 @@ import type { Entitlement, FetchedThreads, FetchedThreadsMore, + FetchPinnedMessagesResponse, FileComponentData, ForumChannel, Guild, @@ -1756,7 +1757,7 @@ declare const guildChannelManager: GuildChannelManager; expectType>>(messages.crosspost('1234567890')); expectType>>(messages.edit('1234567890', 'text')); expectType>>(messages.fetch('1234567890')); - expectType>>>(messages.fetchPinned()); + expectType>>(messages.fetchPins()); expectType(message.guild); expectType(message.guildId); expectType(message.channel.messages.channel); @@ -1769,7 +1770,7 @@ declare const guildChannelManager: GuildChannelManager; expectType(messages); expectType>(messages.edit('1234567890', 'text')); expectType>(messages.fetch('1234567890')); - expectType>>(messages.fetchPinned()); + expectType>(messages.fetchPins()); expectType(message.guild); expectType(message.guildId); expectType(message.channel.messages.channel);