diff --git a/src/structures/Message.js b/src/structures/Message.js index 31727407e..c2101f6b9 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -889,6 +889,14 @@ class Message extends Base { return equal; } + /** + * Whether this message is from a guild. + * @returns {boolean} + */ + inGuild() { + return Boolean(this.guildId); + } + /** * When concatenated with a string, this automatically concatenates the message's content instead of the object. * @returns {string} diff --git a/typings/index.d.ts b/typings/index.d.ts index b934dabae..19a4fdf16 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -270,7 +270,7 @@ export class BaseClient extends EventEmitter { export type GuildCacheMessage = CacheTypeReducer< Cached, - Message, + GuildMessage & Message, APIMessage, Message | APIMessage, Message | APIMessage @@ -315,10 +315,9 @@ export type CacheHelper< Cached extends GuildCacheState, > = T extends InteractionResponsesResolvable ? InteractionResponses & T : GuildInteraction & T; -export type GuildCached = CacheHelper; -export type GuildRaw = CacheHelper; -export type GuildPresent = CacheHelper; - +export type CachedInteraction = CacheHelper; +export type RawInteraction = CacheHelper; +export type PresentInteraction = CacheHelper; export abstract class BaseGuild extends Base { protected constructor(client: Client, data: RawBaseGuildData); public readonly createdAt: Date; @@ -1242,8 +1241,8 @@ export type TaggedUnion = T extends Record : never; // This creates a map of MessageComponentTypes to their respective `InteractionCollectorOptionsResolvable` variant. -export type CollectorOptionsTypeResolver = { - readonly [T in U['componentType']]: TaggedUnion; +export type CollectorOptionsTypeResolver, Cached = boolean> = { + readonly [T in U['componentType']]: TaggedUnion; }; // This basically says "Given a `InteractionCollectorOptionsResolvable` variant", I'll give the corresponding @@ -1254,18 +1253,23 @@ export type ConditionalInteractionCollectorType; // This maps each componentType key to each variant. -export type MappedInteractionCollectorOptions = CollectorOptionsTypeResolver; +export type MappedInteractionCollectorOptions = CollectorOptionsTypeResolver< + InteractionCollectorOptionsResolvable, + Cached +>; // Converts mapped types to complimentary collector types. -export type InteractionCollectorReturnType = - T extends MessageComponentType | MessageComponentTypes - ? ConditionalInteractionCollectorType - : InteractionCollector; +export type InteractionCollectorReturnType< + T extends MessageComponentType | MessageComponentTypes | undefined, + Cached extends boolean = false, +> = T extends MessageComponentType | MessageComponentTypes + ? ConditionalInteractionCollectorType[T]> + : InteractionCollector; export type InteractionExtractor = T extends | MessageComponentType | MessageComponentTypes - ? MappedInteractionCollectorOptions[T] extends InteractionCollectorOptions + ? MappedInteractionCollectorOptions[T] extends InteractionCollectorOptions ? Item : never : MessageComponentInteraction; @@ -1281,6 +1285,23 @@ export type AwaitMessageCollectorOptionsParams >; +export type GuildTextBasedChannel = Exclude; + +export type CachedMessage = GuildMessage<'cached'> & Message; +export interface GuildMessage { + awaitMessageComponent< + T extends MessageComponentType | MessageComponentTypes | undefined = MessageComponentTypes.ACTION_ROW, + >( + options?: AwaitMessageCollectorOptionsParams, + ): Promise & InteractionExtractor>; + + createMessageComponentCollector( + options?: MessageCollectorOptionsParams, + ): InteractionCollectorReturnType; + + readonly channel: CacheTypeReducer; +} + export class Message extends Base { private constructor(client: Client, data: RawMessageData); private _patch(data: RawPartialMessageData | RawMessageData): void; @@ -1350,6 +1371,7 @@ export class Message extends Base { public toJSON(): unknown; public toString(): string; public unpin(): Promise; + public inGuild(): this is GuildMessage<'cached'> & this; } export class MessageActionRow extends BaseMessageComponent { @@ -4247,23 +4269,31 @@ export interface InteractionCollectorOptions extends Coll message?: Message | APIMessage; } -export interface ButtonInteractionCollectorOptions extends MessageComponentCollectorOptions { +export interface ButtonInteractionCollectorOptions + extends MessageComponentCollectorOptions< + Cached extends true ? CachedInteraction : ButtonInteraction + > { componentType: 'BUTTON' | MessageComponentTypes.BUTTON; } -export interface SelectMenuInteractionCollectorOptions extends MessageComponentCollectorOptions { +export interface SelectMenuInteractionCollectorOptions + extends MessageComponentCollectorOptions< + Cached extends true ? CachedInteraction : SelectMenuInteraction + > { componentType: 'SELECT_MENU' | MessageComponentTypes.SELECT_MENU; } -export interface MessageInteractionCollectorOptions - extends MessageComponentCollectorOptions { +export interface MessageInteractionCollectorOptions + extends MessageComponentCollectorOptions< + Cached extends true ? CachedInteraction : MessageComponentInteraction + > { componentType: 'ACTION_ROW' | MessageComponentTypes.ACTION_ROW; } -export type InteractionCollectorOptionsResolvable = - | MessageInteractionCollectorOptions - | SelectMenuInteractionCollectorOptions - | ButtonInteractionCollectorOptions; +export type InteractionCollectorOptionsResolvable = + | MessageInteractionCollectorOptions + | SelectMenuInteractionCollectorOptions + | ButtonInteractionCollectorOptions; export interface InteractionDeferReplyOptions { ephemeral?: boolean; diff --git a/typings/tests.ts b/typings/tests.ts index 8e5b260ca..6f33f7d80 100644 --- a/typings/tests.ts +++ b/typings/tests.ts @@ -33,15 +33,19 @@ import { DMChannel, Guild, GuildApplicationCommandManager, - GuildCached, + CachedInteraction, GuildChannelManager, GuildEmoji, GuildEmojiManager, GuildMember, + GuildMessage, GuildResolvable, + GuildTextBasedChannel, + GuildTextChannelResolvable, Intents, Interaction, InteractionCollector, + InteractionResponses, LimitedCollection, Message, MessageActionRow, @@ -73,6 +77,7 @@ import { Typing, User, VoiceChannel, + CachedMessage, } from '.'; import { ApplicationCommandOptionTypes } from './enums'; @@ -492,6 +497,22 @@ client.on('messageCreate', async message => { assertIsMessage(channel.send({ embeds: [embed] })); assertIsMessage(channel.send({ embeds: [embed], files: [attachment] })); + if (message.inGuild()) { + assertType(message); + const component = await message.awaitMessageComponent({ componentType: 'BUTTON' }); + assertType>(component); + assertType>(component.reply({ fetchReply: true })); + + const buttonCollector = message.createMessageComponentCollector({ componentType: 'BUTTON' }); + assertType>>(buttonCollector); + assertType(message.channel); + } + + assertType(message.channel); + + // @ts-expect-error + assertType(message.channel); + // @ts-expect-error channel.send(); // @ts-expect-error @@ -876,8 +897,8 @@ declare const booleanValue: boolean; if (interaction.inGuild()) assertType(interaction.guildId); client.on('interactionCreate', async interaction => { - const consumeCachedCommand = (_i: GuildCached) => {}; - const consumeCachedInteraction = (_i: GuildCached) => {}; + const consumeCachedCommand = (_i: CachedInteraction) => {}; + const consumeCachedInteraction = (_i: CachedInteraction) => {}; if (interaction.inCachedGuild()) { assertType(interaction.member); @@ -969,6 +990,12 @@ client.on('interactionCreate', async interaction => { assertType(interaction.options.getMember('test')); assertType(interaction.options.getMember('test', true)); } else if (interaction.inCachedGuild()) { + const msg = await interaction.reply({ fetchReply: true }); + const btn = await msg.awaitMessageComponent({ componentType: 'BUTTON' }); + + assertType(msg); + assertType>(btn); + consumeCachedCommand(interaction); assertType(interaction.options.getMember('test', true)); assertType(interaction.options.getMember('test'));