From 715fdbe4e6783d66c40a97de806af3dc6b3022bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=86MB=C3=98?= <69138346+TAEMBO@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:35:58 -0800 Subject: [PATCH] feat: add ChannelManager#createMessage() --- .../discord.js/src/managers/ChannelManager.js | 50 +++++++++++ .../discord.js/src/managers/MessageManager.js | 31 +------ .../discord.js/src/managers/UserManager.js | 4 +- .../discord.js/src/structures/BaseChannel.js | 8 -- .../src/structures/BaseGuildTextChannel.js | 3 +- .../src/structures/BaseGuildVoiceChannel.js | 3 +- .../discord.js/src/structures/DMChannel.js | 3 +- .../discord.js/src/structures/GuildMember.js | 34 ++++---- packages/discord.js/src/structures/Message.js | 55 +----------- .../src/structures/MessagePayload.js | 13 ++- .../src/structures/ThreadChannel.js | 3 +- .../src/structures/ThreadOnlyChannel.js | 3 +- packages/discord.js/src/structures/User.js | 34 ++++---- .../structures/interfaces/TextBasedChannel.js | 86 ++++--------------- packages/discord.js/src/util/Constants.js | 14 --- packages/discord.js/typings/index.d.ts | 33 ++----- packages/discord.js/typings/index.test-d.ts | 48 +++-------- 17 files changed, 134 insertions(+), 291 deletions(-) diff --git a/packages/discord.js/src/managers/ChannelManager.js b/packages/discord.js/src/managers/ChannelManager.js index 0126d914467d5..e027563402fb8 100644 --- a/packages/discord.js/src/managers/ChannelManager.js +++ b/packages/discord.js/src/managers/ChannelManager.js @@ -1,13 +1,17 @@ 'use strict'; const process = require('node:process'); +const { lazy } = require('@discordjs/util'); const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); const { BaseChannel } = require('../structures/BaseChannel'); +const MessagePayload = require('../structures/MessagePayload'); const { createChannel } = require('../util/Channels'); const { ThreadChannelTypes } = require('../util/Constants'); const Events = require('../util/Events'); +const getMessage = lazy(() => require('../structures/Message').Message); + let cacheWarningEmitted = false; /** @@ -123,6 +127,52 @@ class ChannelManager extends CachedManager { const data = await this.client.rest.get(Routes.channel(id)); return this._add(data, null, { cache, allowUnknownGuild }); } + + /** + * Creates a message in a channel. + * @param {TextChannelResolvable} channel The channel to send the message to + * @param {string|MessagePayload|MessageCreateOptions} options The options to provide + * @returns {Promise} + * @example + * // Send a basic message + * client.channels.createMessage(channel, 'hello!') + * .then(message => console.log(`Sent message: ${message.content}`)) + * .catch(console.error); + * @example + * // Send a remote file + * client.channels.createMessage(channel, { + * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] + * }) + * .then(console.log) + * .catch(console.error); + * @example + * // Send a local file + * client.channels.createMessage(channel, { + * files: [{ + * attachment: 'entire/path/to/file.jpg', + * name: 'file.jpg', + * description: 'A description of the file' + * }] + * }) + * .then(console.log) + * .catch(console.error); + */ + async createMessage(channel, options) { + let messagePayload; + + if (options instanceof MessagePayload) { + messagePayload = options.resolveBody(); + } else { + messagePayload = MessagePayload.create(this, options).resolveBody(); + } + + const resolvedChannelId = this.resolveId(channel); + const resolvedChannel = this.resolve(channel); + const { body, files } = await messagePayload.resolveFiles(); + const data = await this.client.rest.post(Routes.channelMessages(resolvedChannelId), { body, files }); + + return resolvedChannel?.messages._add(data) ?? new (getMessage())(this.client, data); + } } module.exports = ChannelManager; diff --git a/packages/discord.js/src/managers/MessageManager.js b/packages/discord.js/src/managers/MessageManager.js index a2c44c4463521..dcddf28573836 100644 --- a/packages/discord.js/src/managers/MessageManager.js +++ b/packages/discord.js/src/managers/MessageManager.js @@ -2,9 +2,9 @@ const { Collection } = require('@discordjs/collection'); const { makeURLSearchParams } = require('@discordjs/rest'); -const { MessageReferenceType, Routes } = require('discord-api-types/v10'); +const { Routes } = require('discord-api-types/v10'); const CachedManager = require('./CachedManager'); -const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors'); +const { DiscordjsTypeError, ErrorCodes } = require('../errors'); const { Message } = require('../structures/Message'); const MessagePayload = require('../structures/MessagePayload'); const { MakeCacheOverrideSymbol } = require('../util/Symbols'); @@ -209,33 +209,6 @@ class MessageManager extends CachedManager { return this.cache.get(data.id) ?? this._add(data); } - /** - * Forwards a message to this manager's channel. - * @param {Message|MessageReference} reference The message to forward - * @returns {Promise} - */ - async forward(reference) { - if (!reference) throw new DiscordjsError(ErrorCodes.MessageReferenceMissing); - const message_id = this.resolveId(reference.messageId); - if (!message_id) throw new DiscordjsError(ErrorCodes.MessageReferenceMissing); - const channel_id = this.client.channels.resolveId(reference.channelId); - if (!channel_id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'ChannelResolvable'); - const guild_id = this.client.guilds.resolveId(reference.guildId); - - const data = await this.client.rest.post(Routes.channelMessages(this.channel.id), { - body: { - message_reference: { - message_id, - channel_id, - guild_id, - type: MessageReferenceType.Forward, - }, - }, - }); - - return this.cache.get(data.id) ?? this._add(data); - } - /** * Pins a message to the channel's pinned messages, even if it's not cached. * @param {MessageResolvable} message The message to pin diff --git a/packages/discord.js/src/managers/UserManager.js b/packages/discord.js/src/managers/UserManager.js index b0026da2c69a7..34af6b0b63e11 100644 --- a/packages/discord.js/src/managers/UserManager.js +++ b/packages/discord.js/src/managers/UserManager.js @@ -102,7 +102,9 @@ class UserManager extends CachedManager { * @returns {Promise} */ async send(user, options) { - return (await this.createDM(user)).send(options); + const dmChannel = await this.createDM(user); + + return this.client.channels.createMessage(dmChannel, options); } /** diff --git a/packages/discord.js/src/structures/BaseChannel.js b/packages/discord.js/src/structures/BaseChannel.js index ebb95682819c8..2b179d3574bc8 100644 --- a/packages/discord.js/src/structures/BaseChannel.js +++ b/packages/discord.js/src/structures/BaseChannel.js @@ -155,14 +155,6 @@ class BaseChannel extends Base { return 'availableTags' in this; } - /** - * Indicates whether this channel is sendable. - * @returns {boolean} - */ - isSendable() { - return 'send' in this; - } - toJSON(...props) { return super.toJSON({ createdTimestamp: true }, ...props); } diff --git a/packages/discord.js/src/structures/BaseGuildTextChannel.js b/packages/discord.js/src/structures/BaseGuildTextChannel.js index c271565d750a6..2ff78cb32957a 100644 --- a/packages/discord.js/src/structures/BaseGuildTextChannel.js +++ b/packages/discord.js/src/structures/BaseGuildTextChannel.js @@ -178,7 +178,6 @@ class BaseGuildTextChannel extends GuildChannel { /* eslint-disable no-empty-function */ get lastMessage() {} get lastPinAt() {} - send() {} sendTyping() {} createMessageCollector() {} awaitMessages() {} @@ -191,6 +190,6 @@ class BaseGuildTextChannel extends GuildChannel { setNSFW() {} } -TextBasedChannel.applyToClass(BaseGuildTextChannel, true); +TextBasedChannel.applyToClass(BaseGuildTextChannel); module.exports = BaseGuildTextChannel; diff --git a/packages/discord.js/src/structures/BaseGuildVoiceChannel.js b/packages/discord.js/src/structures/BaseGuildVoiceChannel.js index 220ac6c8d83b2..97adfe1f61711 100644 --- a/packages/discord.js/src/structures/BaseGuildVoiceChannel.js +++ b/packages/discord.js/src/structures/BaseGuildVoiceChannel.js @@ -216,7 +216,6 @@ class BaseGuildVoiceChannel extends GuildChannel { // These are here only for documentation purposes - they are implemented by TextBasedChannel /* eslint-disable no-empty-function */ get lastMessage() {} - send() {} sendTyping() {} createMessageCollector() {} awaitMessages() {} @@ -229,6 +228,6 @@ class BaseGuildVoiceChannel extends GuildChannel { setNSFW() {} } -TextBasedChannel.applyToClass(BaseGuildVoiceChannel, true, ['lastPinAt']); +TextBasedChannel.applyToClass(BaseGuildVoiceChannel, ['lastPinAt']); module.exports = BaseGuildVoiceChannel; diff --git a/packages/discord.js/src/structures/DMChannel.js b/packages/discord.js/src/structures/DMChannel.js index ee466442a2190..92e756099817b 100644 --- a/packages/discord.js/src/structures/DMChannel.js +++ b/packages/discord.js/src/structures/DMChannel.js @@ -105,7 +105,6 @@ class DMChannel extends BaseChannel { /* eslint-disable no-empty-function */ get lastMessage() {} get lastPinAt() {} - send() {} sendTyping() {} createMessageCollector() {} awaitMessages() {} @@ -118,7 +117,7 @@ class DMChannel extends BaseChannel { // Doesn't work on DM channels; setNSFW() {} } -TextBasedChannel.applyToClass(DMChannel, true, [ +TextBasedChannel.applyToClass(DMChannel, [ 'bulkDelete', 'fetchWebhooks', 'createWebhook', diff --git a/packages/discord.js/src/structures/GuildMember.js b/packages/discord.js/src/structures/GuildMember.js index b1f1b52834609..8ff334674ff86 100644 --- a/packages/discord.js/src/structures/GuildMember.js +++ b/packages/discord.js/src/structures/GuildMember.js @@ -3,7 +3,6 @@ const { PermissionFlagsBits } = require('discord-api-types/v10'); const Base = require('./Base'); const VoiceState = require('./VoiceState'); -const TextBasedChannel = require('./interfaces/TextBasedChannel'); const { DiscordjsError, ErrorCodes } = require('../errors'); const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField'); @@ -11,7 +10,6 @@ const PermissionsBitField = require('../util/PermissionsBitField'); /** * Represents a member of a guild on Discord. - * @implements {TextBasedChannel} * @extends {Base} */ class GuildMember extends Base { @@ -478,6 +476,22 @@ class GuildMember extends Base { return this.guild.members.fetch({ user: this.id, cache: true, force }); } + /** + * Sends a message to this user. + * @param {string|MessagePayload|MessageCreateOptions} options The options to provide + * @returns {Promise} + * @example + * // Send a direct message + * guildMember.send('Hello!') + * .then(message => console.log(`Sent message: ${message.content} to ${guildMember.displayName}`)) + * .catch(console.error); + */ + async send(options) { + const dmChannel = await this.createDM(); + + return this.client.channels.createMessage(dmChannel, options); + } + /** * Whether this guild member equals another guild member. It compares all properties, so for most * comparison it is advisable to just compare `member.id === member2.id` as it is significantly faster @@ -529,20 +543,4 @@ class GuildMember extends Base { } } -/** - * Sends a message to this user. - * @method send - * @memberof GuildMember - * @instance - * @param {string|MessagePayload|MessageCreateOptions} options The options to provide - * @returns {Promise} - * @example - * // Send a direct message - * guildMember.send('Hello!') - * .then(message => console.log(`Sent message: ${message.content} to ${guildMember.displayName}`)) - * .catch(console.error); - */ - -TextBasedChannel.applyToClass(GuildMember); - exports.GuildMember = GuildMember; diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 2bb6d0dc23aba..f2bdc64f1e8f9 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -8,7 +8,6 @@ const { ChannelType, MessageType, MessageFlags, - MessageReferenceType, PermissionFlagsBits, } = require('discord-api-types/v10'); const Attachment = require('./Attachment'); @@ -17,11 +16,10 @@ const ClientApplication = require('./ClientApplication'); const Embed = require('./Embed'); const InteractionCollector = require('./InteractionCollector'); const Mentions = require('./MessageMentions'); -const MessagePayload = require('./MessagePayload'); const { Poll } = require('./Poll.js'); const ReactionCollector = require('./ReactionCollector'); const { Sticker } = require('./Sticker'); -const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors'); +const { DiscordjsError, ErrorCodes } = require('../errors'); const ReactionManager = require('../managers/ReactionManager'); const { createComponent } = require('../util/Components'); const { NonSystemMessageTypes, MaxBulkDeletableMessageAge, UndeletableMessageTypes } = require('../util/Constants'); @@ -795,20 +793,6 @@ class Message extends Base { return message; } - /** - * Forwards this message. - * @param {ChannelResolvable} channel The channel to forward this message to - * @returns {Promise} - */ - async forward(channel) { - const resolvedChannel = this.client.channels.resolve(channel); - - if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'ChannelResolvable'); - - const message = await resolvedChannel.messages.forward(this); - return message; - } - /** * Whether the message is crosspostable by the client user * @type {boolean} @@ -937,43 +921,6 @@ class Message extends Base { return this; } - /** - * Options provided when sending a message as an inline reply. - * @typedef {BaseMessageCreateOptions} MessageReplyOptions - * @property {boolean} [failIfNotExists=this.client.options.failIfNotExists] Whether to error if the referenced - * message does not exist (creates a standard message in this case when false) - */ - - /** - * Send an inline reply to this message. - * @param {string|MessagePayload|MessageReplyOptions} options The options to provide - * @returns {Promise} - * @example - * // Reply to a message - * message.reply('This is a reply!') - * .then(() => console.log(`Replied to message "${message.content}"`)) - * .catch(console.error); - */ - reply(options) { - if (!this.channel) return Promise.reject(new DiscordjsError(ErrorCodes.ChannelNotCached)); - let data; - - if (options instanceof MessagePayload) { - data = options; - } else { - data = MessagePayload.create(this, options, { - messageReference: { - messageId: this.id, - channelId: this.channelId, - guildId: this.guildId, - type: MessageReferenceType.Default, - failIfNotExists: options?.failIfNotExists ?? this.client.options.failIfNotExists, - }, - }); - } - return this.channel.send(data); - } - /** * Options for starting a thread on a message. * @typedef {Object} StartThreadOptions diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index 358ce04cf6064..3d37296273081 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -173,15 +173,12 @@ class MessagePayload { let message_reference; if (this.options.messageReference) { const reference = this.options.messageReference; - const message_id = this.target.messages.resolveId(reference.messageId); - const channel_id = this.target.client.channels.resolveId(reference.channelId); - const guild_id = this.target.client.guilds.resolveId(reference.guildId); - if (message_id) { + if (reference.messageId) { message_reference = { - message_id, - channel_id, - guild_id, + message_id: reference.messageId, + channel_id: reference.channelId, + guild_id: reference.guildId, type: reference.type, fail_if_not_exists: reference.failIfNotExists ?? this.target.client.options.failIfNotExists, }; @@ -301,7 +298,7 @@ module.exports = MessagePayload; /** * A target for a message. - * @typedef {TextBasedChannels|User|GuildMember|Webhook|WebhookClient|BaseInteraction|InteractionWebhook| + * @typedef {ChannelManager|Webhook|WebhookClient|BaseInteraction|InteractionWebhook| * Message|MessageManager} MessageTarget */ diff --git a/packages/discord.js/src/structures/ThreadChannel.js b/packages/discord.js/src/structures/ThreadChannel.js index d3b92dd80592b..7bff984fb889b 100644 --- a/packages/discord.js/src/structures/ThreadChannel.js +++ b/packages/discord.js/src/structures/ThreadChannel.js @@ -596,7 +596,6 @@ class ThreadChannel extends BaseChannel { /* eslint-disable no-empty-function */ get lastMessage() {} get lastPinAt() {} - send() {} sendTyping() {} createMessageCollector() {} awaitMessages() {} @@ -607,6 +606,6 @@ class ThreadChannel extends BaseChannel { // Doesn't work on Thread channels; setNSFW() {} } -TextBasedChannel.applyToClass(ThreadChannel, true, ['fetchWebhooks', 'setRateLimitPerUser', 'setNSFW']); +TextBasedChannel.applyToClass(ThreadChannel, ['fetchWebhooks', 'setRateLimitPerUser', 'setNSFW']); module.exports = ThreadChannel; diff --git a/packages/discord.js/src/structures/ThreadOnlyChannel.js b/packages/discord.js/src/structures/ThreadOnlyChannel.js index 0445b9988e9ee..840fd8462fac6 100644 --- a/packages/discord.js/src/structures/ThreadOnlyChannel.js +++ b/packages/discord.js/src/structures/ThreadOnlyChannel.js @@ -234,8 +234,7 @@ class ThreadOnlyChannel extends GuildChannel { setRateLimitPerUser() {} } -TextBasedChannel.applyToClass(ThreadOnlyChannel, true, [ - 'send', +TextBasedChannel.applyToClass(ThreadOnlyChannel, [ 'lastMessage', 'lastPinAt', 'bulkDelete', diff --git a/packages/discord.js/src/structures/User.js b/packages/discord.js/src/structures/User.js index 6025410c30f19..7ea59929ac57d 100644 --- a/packages/discord.js/src/structures/User.js +++ b/packages/discord.js/src/structures/User.js @@ -4,12 +4,10 @@ const { userMention } = require('@discordjs/formatters'); const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const Base = require('./Base'); -const TextBasedChannel = require('./interfaces/TextBasedChannel'); const UserFlagsBitField = require('../util/UserFlagsBitField'); /** * Represents a user on Discord. - * @implements {TextBasedChannel} * @extends {Base} */ class User extends Base { @@ -277,6 +275,22 @@ class User extends Base { return this.client.users.deleteDM(this.id); } + /** + * Sends a message to this user. + * @param {string|MessagePayload|MessageCreateOptions} options The options to provide + * @returns {Promise} + * @example + * // Send a direct message + * user.send('Hello!') + * .then(message => console.log(`Sent message: ${message.content} to ${user.tag}`)) + * .catch(console.error); + */ + async send(options) { + const dmChannel = await this.createDM(); + + return this.client.channels.createMessage(dmChannel, options); + } + /** * Checks if the user is equal to another. * It compares id, username, discriminator, avatar, banner, accent color, and bot flags. @@ -361,20 +375,4 @@ class User extends Base { } } -/** - * Sends a message to this user. - * @method send - * @memberof User - * @instance - * @param {string|MessagePayload|MessageCreateOptions} options The options to provide - * @returns {Promise} - * @example - * // Send a direct message - * user.send('Hello!') - * .then(message => console.log(`Sent message: ${message.content} to ${user.tag}`)) - * .catch(console.error); - */ - -TextBasedChannel.applyToClass(User); - module.exports = User; diff --git a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js index b039197041f7e..4d3c21b23011e 100644 --- a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js +++ b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js @@ -7,7 +7,6 @@ const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors const { MaxBulkDeletableMessageAge } = require('../../util/Constants'); const InteractionCollector = require('../InteractionCollector'); const MessageCollector = require('../MessageCollector'); -const MessagePayload = require('../MessagePayload'); /** * Interface for classes that have text-channel-like features. @@ -133,57 +132,6 @@ class TextBasedChannel { * @typedef {string} MessageMentionTypes */ - /** - * Sends a message to this channel. - * @param {string|MessagePayload|MessageCreateOptions} options The options to provide - * @returns {Promise} - * @example - * // Send a basic message - * channel.send('hello!') - * .then(message => console.log(`Sent message: ${message.content}`)) - * .catch(console.error); - * @example - * // Send a remote file - * channel.send({ - * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] - * }) - * .then(console.log) - * .catch(console.error); - * @example - * // Send a local file - * channel.send({ - * files: [{ - * attachment: 'entire/path/to/file.jpg', - * name: 'file.jpg', - * description: 'A description of the file' - * }] - * }) - * .then(console.log) - * .catch(console.error); - */ - async send(options) { - const User = require('../User'); - const { GuildMember } = require('../GuildMember'); - - if (this instanceof User || this instanceof GuildMember) { - const dm = await this.createDM(); - return dm.send(options); - } - - let messagePayload; - - if (options instanceof MessagePayload) { - messagePayload = options.resolveBody(); - } else { - messagePayload = MessagePayload.create(this, options).resolveBody(); - } - - const { body, files } = await messagePayload.resolveFiles(); - const d = await this.client.rest.post(Routes.channelMessages(this.id), { body, files }); - - return this.messages.cache.get(d.id) ?? this.messages._add(d); - } - /** * Sends a typing indicator in the channel. * @returns {Promise} Resolves upon the typing status being sent @@ -398,24 +346,22 @@ class TextBasedChannel { return this.edit({ nsfw, reason }); } - static applyToClass(structure, full = false, ignore = []) { - const props = ['send']; - if (full) { - props.push( - 'lastMessage', - 'lastPinAt', - 'bulkDelete', - 'sendTyping', - 'createMessageCollector', - 'awaitMessages', - 'createMessageComponentCollector', - 'awaitMessageComponent', - 'fetchWebhooks', - 'createWebhook', - 'setRateLimitPerUser', - 'setNSFW', - ); - } + static applyToClass(structure, ignore = []) { + const props = [ + 'lastMessage', + 'lastPinAt', + 'bulkDelete', + 'sendTyping', + 'createMessageCollector', + 'awaitMessages', + 'createMessageComponentCollector', + 'awaitMessageComponent', + 'fetchWebhooks', + 'createWebhook', + 'setRateLimitPerUser', + 'setNSFW', + ]; + for (const prop of props) { if (ignore.includes(prop)) continue; Object.defineProperty( diff --git a/packages/discord.js/src/util/Constants.js b/packages/discord.js/src/util/Constants.js index c4209cb3c4bce..487c84cef43d3 100644 --- a/packages/discord.js/src/util/Constants.js +++ b/packages/discord.js/src/util/Constants.js @@ -122,20 +122,6 @@ exports.GuildTextBasedChannelTypes = [ */ exports.TextBasedChannelTypes = [...exports.GuildTextBasedChannelTypes, ChannelType.DM, ChannelType.GroupDM]; -/** - * The types of channels that are text-based and can have messages sent into. The available types are: - * * {@link ChannelType.DM} - * * {@link ChannelType.GuildText} - * * {@link ChannelType.GuildAnnouncement} - * * {@link ChannelType.AnnouncementThread} - * * {@link ChannelType.PublicThread} - * * {@link ChannelType.PrivateThread} - * * {@link ChannelType.GuildVoice} - * * {@link ChannelType.GuildStageVoice} - * @typedef {ChannelType[]} SendableChannels - */ -exports.SendableChannels = [...exports.GuildTextBasedChannelTypes, ChannelType.DM]; - /** * The types of channels that are threads. The available types are: * * {@link ChannelType.AnnouncementThread} diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index f44fe4b74766b..fcc57769721bb 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -955,7 +955,6 @@ export abstract class BaseChannel extends Base { public isDMBased(): this is PartialGroupDMChannel | DMChannel | PartialDMChannel; public isVoiceBased(): this is VoiceBasedChannel; public isThreadOnly(): this is ThreadOnlyChannel; - public isSendable(): this is SendableChannels; public toString(): ChannelMention | UserMention; } @@ -1650,7 +1649,6 @@ export class GuildMemberFlagsBitField extends BitField { public static resolve(bit?: BitFieldResolvable): number; } -export interface GuildMember extends PartialTextBasedChannelFields {} export class GuildMember extends Base { private constructor(client: Client, data: RawGuildMemberData, guild: Guild); private _roles: Snowflake[]; @@ -1692,6 +1690,7 @@ export class GuildMember extends Base { public displayAvatarURL(options?: ImageURLOptions): string; public displayBannerURL(options?: ImageURLOptions): string | null; public edit(options: GuildMemberEditOptions): Promise; + public send(options: string | MessagePayload | MessageCreateOptions): Promise>; public isCommunicationDisabled(): this is GuildMember & { communicationDisabledUntilTimestamp: number; readonly communicationDisabledUntil: Date; @@ -2242,7 +2241,6 @@ export class Message extends Base { public equals(message: Message, rawData: unknown): boolean; public fetchReference(): Promise>>; public fetchWebhook(): Promise; - public forward(channel: TextBasedChannelResolvable): Promise; public crosspost(): Promise>>; public fetch(force?: boolean): Promise>>; public pin(reason?: string): Promise>>; @@ -2633,7 +2631,6 @@ export interface DefaultReactionEmoji { export interface ThreadOnlyChannel extends Omit< TextBasedChannelFields, - | 'send' | 'lastMessage' | 'lastPinAt' | 'bulkDelete' @@ -3447,8 +3444,6 @@ export interface AvatarDecorationData { skuId: Snowflake; } -// tslint:disable-next-line no-empty-interface -export interface User extends PartialTextBasedChannelFields {} export class User extends Base { protected constructor(client: Client, data: RawUserData); private _equals(user: APIUser): boolean; @@ -3481,6 +3476,7 @@ export class User extends Base { public equals(user: User): boolean; public fetch(force?: boolean): Promise; public toString(): UserMention; + public send(options: string | MessagePayload | MessageCreateOptions): Promise>; } export class UserContextMenuCommandInteraction< @@ -3812,7 +3808,6 @@ export const Constants: { SweeperKeys: SweeperKey[]; NonSystemMessageTypes: NonSystemMessageType[]; TextBasedChannelTypes: TextBasedChannelTypes[]; - SendableChannels: SendableChannelTypes[]; GuildTextBasedChannelTypes: GuildTextBasedChannelTypes[]; ThreadChannelTypes: ThreadChannelType[]; VoiceBasedChannelTypes: VoiceBasedChannelTypes[]; @@ -4131,6 +4126,10 @@ export class CategoryChannelChildManager extends DataManager { private constructor(client: Client, iterable: Iterable); + public createMessage( + channel: Omit, + options: string | MessagePayload | MessageCreateOptions, + ): Promise; public fetch(id: Snowflake, options?: FetchChannelOptions): Promise; } @@ -4419,7 +4418,6 @@ export abstract class MessageManager extends public fetch(options: MessageResolvable | FetchMessageOptions): Promise>; public fetch(options?: FetchMessagesOptions): Promise>>; public fetchPinned(cache?: boolean): Promise>>; - public forward(reference: Omit): Promise>; public react(message: MessageResolvable, emoji: EmojiIdentifierResolvable): Promise; public pin(message: MessageResolvable, reason?: string): Promise; public unpin(message: MessageResolvable, reason?: string): Promise; @@ -4591,12 +4589,7 @@ export class VoiceStateManager extends CachedManager = abstract new (...args: any[]) => Entity; -export interface PartialTextBasedChannelFields { - send(options: string | MessagePayload | MessageCreateOptions): Promise>; -} - -export interface TextBasedChannelFields - extends PartialTextBasedChannelFields { +export interface TextBasedChannelFields { lastMessageId: Snowflake | null; get lastMessage(): Message | null; lastPinTimestamp: number | null; @@ -6502,15 +6495,11 @@ export interface TextInputComponentData extends BaseComponentData { } export type MessageTarget = + | ChannelManager | Interaction | InteractionWebhook - | TextBasedChannel - | User - | GuildMember | Webhook - | WebhookClient - | Message - | MessageManager; + | WebhookClient; export interface MultipleShardRespawnOptions { shardDelay?: number; @@ -6814,16 +6803,12 @@ export type Channel = export type TextBasedChannel = Exclude, ForumChannel | MediaChannel>; -export type SendableChannels = Extract any }>; - export type TextBasedChannels = TextBasedChannel; export type TextBasedChannelTypes = TextBasedChannel['type']; export type GuildTextBasedChannelTypes = Exclude; -export type SendableChannelTypes = SendableChannels['type']; - export type VoiceBasedChannel = Extract; export type GuildBasedChannel = Extract; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 4c3da4abb0026..6916d40689bde 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -74,7 +74,6 @@ import { ModalBuilder, AnnouncementChannel, Options, - PartialTextBasedChannelFields, PartialUser, PermissionsBitField, ReactionCollector, @@ -85,7 +84,6 @@ import { ShardingManager, Snowflake, StageChannel, - TextBasedChannelFields, type TextBasedChannel, type TextBasedChannelTypes, type VoiceBasedChannel, @@ -206,7 +204,6 @@ import { ApplicationEmojiManager, StickerPack, GuildScheduledEventManager, - SendableChannels, PollData, InteractionCallbackResponse, } from '.'; @@ -419,15 +416,15 @@ client.on('messageCreate', async message => { } expectType>(client); - assertIsMessage(channel.send('string')); - assertIsMessage(channel.send({})); - assertIsMessage(channel.send({ embeds: [] })); + assertIsMessage(client.channels.createMessage(channel, 'string')); + assertIsMessage(client.channels.createMessage(channel, {})); + assertIsMessage(client.channels.createMessage(channel, { embeds: [] })); const attachment = new AttachmentBuilder('file.png'); const embed = new EmbedBuilder(); - assertIsMessage(channel.send({ files: [attachment] })); - assertIsMessage(channel.send({ embeds: [embed] })); - assertIsMessage(channel.send({ embeds: [embed], files: [attachment] })); + assertIsMessage(client.channels.createMessage(channel, { files: [attachment] })); + assertIsMessage(client.channels.createMessage(channel, { embeds: [embed] })); + assertIsMessage(client.channels.createMessage(channel, { embeds: [embed], files: [attachment] })); if (message.inGuild()) { expectAssignable>(message); @@ -456,9 +453,9 @@ client.on('messageCreate', async message => { expectNotType(message.channel); // @ts-expect-error - channel.send(); + client.channels.createMessage(); // @ts-expect-error - channel.send({ another: 'property' }); + client.channels.createMessage({ another: 'property' }); // Check collector creations. @@ -620,7 +617,7 @@ client.on('messageCreate', async message => { const embedData = { description: 'test', color: 0xff0000 }; - channel.send({ + client.channels.createMessage(channel, { components: [row, rawButtonsRow, buttonsRow, rawStringSelectMenuRow, stringSelectRow], embeds: [embed, embedData], }); @@ -1266,7 +1263,7 @@ client.on('guildCreate', async g => { ], }); - channel.send({ components: [row, row2] }); + client.channels.createMessage(channel, { components: [row, row2] }); } channel.setName('foo').then(updatedChannel => { @@ -1375,15 +1372,6 @@ expectType(threadChannelFromForum.parent); expectType(threadChannelNotFromForum.parent); -// Test whether the structures implement send -expectType['send']>(dmChannel.send); -expectType['send']>(threadChannel.send); -expectType['send']>(announcementChannel.send); -expectType['send']>(textChannel.send); -expectType['send']>(voiceChannel.send); -expectAssignable(user); -expectAssignable(guildMember); - expectType>(textChannel.setType(ChannelType.GuildAnnouncement)); expectType>(announcementChannel.setType(ChannelType.GuildText)); @@ -2542,7 +2530,7 @@ declare const sku: SKU; }); } -await textChannel.send({ +await client.channels.createMessage('123', { poll: { question: { text: 'Question', @@ -2582,19 +2570,5 @@ expectType>(await client.fetchStickerPacks()) expectType>(await client.fetchStickerPacks({})); expectType(await client.fetchStickerPacks({ packId: snowflake })); -client.on('interactionCreate', interaction => { - if (!interaction.channel) { - return; - } - - // @ts-expect-error - interaction.channel.send(); - - if (interaction.channel.isSendable()) { - expectType(interaction.channel); - interaction.channel.send({ embeds: [] }); - } -}); - declare const guildScheduledEventManager: GuildScheduledEventManager; await guildScheduledEventManager.edit(snowflake, { recurrenceRule: null });