diff --git a/packages/discord.js/src/managers/ChannelManager.js b/packages/discord.js/src/managers/ChannelManager.js index 2d39a318e..f29a3213e 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.js'); const { BaseChannel } = require('../structures/BaseChannel.js'); +const { MessagePayload } = require('../structures/MessagePayload.js'); const { createChannel } = require('../util/Channels.js'); const { ThreadChannelTypes } = require('../util/Constants.js'); const { Events } = require('../util/Events.js'); +const getMessage = lazy(() => require('../structures/Message.js').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://github.com/discordjs.png'] + * }) + * .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); + } } exports.ChannelManager = ChannelManager; diff --git a/packages/discord.js/src/structures/BaseGuildTextChannel.js b/packages/discord.js/src/structures/BaseGuildTextChannel.js index 1cc5c3118..980fb86a2 100644 --- a/packages/discord.js/src/structures/BaseGuildTextChannel.js +++ b/packages/discord.js/src/structures/BaseGuildTextChannel.js @@ -191,6 +191,6 @@ class BaseGuildTextChannel extends GuildChannel { setNSFW() {} } -TextBasedChannel.applyToClass(BaseGuildTextChannel, true); +TextBasedChannel.applyToClass(BaseGuildTextChannel); exports.BaseGuildTextChannel = BaseGuildTextChannel; diff --git a/packages/discord.js/src/structures/BaseGuildVoiceChannel.js b/packages/discord.js/src/structures/BaseGuildVoiceChannel.js index 15c7b483d..c42df6476 100644 --- a/packages/discord.js/src/structures/BaseGuildVoiceChannel.js +++ b/packages/discord.js/src/structures/BaseGuildVoiceChannel.js @@ -229,6 +229,6 @@ class BaseGuildVoiceChannel extends GuildChannel { setNSFW() {} } -TextBasedChannel.applyToClass(BaseGuildVoiceChannel, true, ['lastPinAt']); +TextBasedChannel.applyToClass(BaseGuildVoiceChannel, ['lastPinAt']); exports.BaseGuildVoiceChannel = BaseGuildVoiceChannel; diff --git a/packages/discord.js/src/structures/DMChannel.js b/packages/discord.js/src/structures/DMChannel.js index faabe06cf..a77d56f91 100644 --- a/packages/discord.js/src/structures/DMChannel.js +++ b/packages/discord.js/src/structures/DMChannel.js @@ -118,7 +118,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 669230a41..c940ff126 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.js'); const { VoiceState } = require('./VoiceState.js'); -const { TextBasedChannel } = require('./interfaces/TextBasedChannel.js'); const { DiscordjsError, ErrorCodes } = require('../errors/index.js'); const { GuildMemberRoleManager } = require('../managers/GuildMemberRoleManager.js'); const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField.js'); @@ -11,7 +10,6 @@ const { PermissionsBitField } = require('../util/PermissionsBitField.js'); /** * Represents a member of a guild on Discord. - * @implements {TextBasedChannel} * @extends {Base} */ class GuildMember extends Base { @@ -476,6 +474,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 @@ -527,20 +541,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 11231c91e..b77ba32b7 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -8,6 +8,7 @@ const { ChannelType, MessageType, MessageFlags, + MessageReferenceType, PermissionFlagsBits, } = require('discord-api-types/v10'); const { Attachment } = require('./Attachment.js'); @@ -680,7 +681,11 @@ class Message extends Base { * @readonly */ get editable() { - const precheck = Boolean(this.author.id === this.client.user.id && (!this.guild || this.channel?.viewable)); + const precheck = Boolean( + this.author.id === this.client.user.id && + (!this.guild || this.channel?.viewable) && + this.reference?.type !== MessageReferenceType.Forward, + ); // Regardless of permissions thread messages cannot be edited if // the thread is archived or the thread is locked and the bot does not have permission to manage threads. @@ -915,21 +920,39 @@ class Message extends Base { * .then(() => console.log(`Replied to message "${message.content}"`)) * .catch(console.error); */ - async reply(options) { - if (!this.channel) throw new DiscordjsError(ErrorCodes.ChannelNotCached); + reply(options) { let data; if (options instanceof MessagePayload) { data = options; } else { data = MessagePayload.create(this, options, { - reply: { - messageReference: this, + messageReference: { + messageId: this.id, + channelId: this.channelId, + guildId: this.guildId ?? undefined, + type: MessageReferenceType.Default, failIfNotExists: options?.failIfNotExists ?? this.client.options.failIfNotExists, }, }); } - return this.channel.send(data); + return this.client.channels.createMessage(this.channelId, data); + } + + /** + * Forwards this message. + * @param {TextChannelResolvable} channel The channel to forward this message to. + * @returns {Promise} + */ + forward(channel) { + return this.client.channels.createMessage(channel, { + messageReference: { + messageId: this.id, + channelId: this.channelId, + guildId: this.guildId ?? undefined, + type: MessageReferenceType.Forward, + }, + }); } /** diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index a5382901e..223a063e6 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -151,7 +151,7 @@ class MessagePayload { if ( // eslint-disable-next-line eqeqeq this.options.flags != null || - (this.isMessage && this.options.reply === undefined) || + (this.isMessage && this.options.messageReference === undefined) || this.isMessageManager ) { flags = new MessageFlagsBitField(this.options.flags).bitfield; @@ -168,13 +168,16 @@ class MessagePayload { } let message_reference; - if (typeof this.options.reply === 'object') { - const reference = this.options.reply.messageReference; - const message_id = this.isMessage ? (reference.id ?? reference) : this.target.messages.resolveId(reference); - if (message_id) { + if (this.options.messageReference) { + const reference = this.options.messageReference; + + if (reference.messageId) { message_reference = { - message_id, - fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists, + 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, }; } } @@ -292,7 +295,7 @@ exports.MessagePayload = MessagePayload; /** * A target for a message. - * @typedef {TextBasedChannels|User|GuildMember|Webhook|WebhookClient|BaseInteraction|InteractionWebhook| + * @typedef {TextBasedChannels|ChannelManager|Webhook|WebhookClient|BaseInteraction|InteractionWebhook| * Message|MessageManager} MessageTarget */ diff --git a/packages/discord.js/src/structures/PartialGroupDMChannel.js b/packages/discord.js/src/structures/PartialGroupDMChannel.js index 7055f5d9f..ceaf60217 100644 --- a/packages/discord.js/src/structures/PartialGroupDMChannel.js +++ b/packages/discord.js/src/structures/PartialGroupDMChannel.js @@ -116,7 +116,7 @@ class PartialGroupDMChannel extends BaseChannel { awaitMessageComponent() {} } -TextBasedChannel.applyToClass(PartialGroupDMChannel, true, [ +TextBasedChannel.applyToClass(PartialGroupDMChannel, [ 'bulkDelete', 'send', 'sendTyping', diff --git a/packages/discord.js/src/structures/ThreadChannel.js b/packages/discord.js/src/structures/ThreadChannel.js index 30bd74214..1467c1c03 100644 --- a/packages/discord.js/src/structures/ThreadChannel.js +++ b/packages/discord.js/src/structures/ThreadChannel.js @@ -598,6 +598,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']); exports.ThreadChannel = ThreadChannel; diff --git a/packages/discord.js/src/structures/ThreadOnlyChannel.js b/packages/discord.js/src/structures/ThreadOnlyChannel.js index 12d7d488c..e7bfbdb17 100644 --- a/packages/discord.js/src/structures/ThreadOnlyChannel.js +++ b/packages/discord.js/src/structures/ThreadOnlyChannel.js @@ -234,7 +234,7 @@ class ThreadOnlyChannel extends GuildChannel { setRateLimitPerUser() {} } -TextBasedChannel.applyToClass(ThreadOnlyChannel, true, [ +TextBasedChannel.applyToClass(ThreadOnlyChannel, [ 'send', 'lastMessage', 'lastPinAt', diff --git a/packages/discord.js/src/structures/User.js b/packages/discord.js/src/structures/User.js index 76f3c1d49..77be3c18f 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.js'); -const { TextBasedChannel } = require('./interfaces/TextBasedChannel.js'); const { UserFlagsBitField } = require('../util/UserFlagsBitField.js'); /** * 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); - exports.User = User; diff --git a/packages/discord.js/src/structures/Webhook.js b/packages/discord.js/src/structures/Webhook.js index 906b96269..ec5c154ed 100644 --- a/packages/discord.js/src/structures/Webhook.js +++ b/packages/discord.js/src/structures/Webhook.js @@ -172,7 +172,7 @@ class Webhook { * @example * // Send a remote file * webhook.send({ - * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] + * files: ['https://github.com/discordjs.png'] * }) * .then(console.log) * .catch(console.error); diff --git a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js index 24142fc35..d7ab76b6e 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.js'); const { InteractionCollector } = require('../InteractionCollector.js'); const { MessageCollector } = require('../MessageCollector.js'); -const { MessagePayload } = require('../MessagePayload.js'); /** * Interface for classes that have text-channel-like features. @@ -88,14 +87,6 @@ class TextBasedChannel { * @property {PollData} [poll] The poll to send with the message */ - /** - * Options for sending a message with a reply. - * @typedef {Object} ReplyOptions - * @property {MessageResolvable} messageReference The message to reply to (must be in the same channel and not system) - * @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) - */ - /** * The options for sending a message. * @typedef {BaseMessageOptionsWithPoll} BaseMessageCreateOptions @@ -110,10 +101,16 @@ class TextBasedChannel { * Only `MessageFlags.SuppressEmbeds` and `MessageFlags.SuppressNotifications` can be set. */ + /** + * @typedef {MessageReference} MessageReferenceOptions + * @property {boolean} [failIfNotExists=this.client.options.failIfNotExists] Whether to error if the + * referenced message doesn't exist instead of sending as a normal (non-reply) message + */ + /** * The options for sending a message. * @typedef {BaseMessageCreateOptions} MessageCreateOptions - * @property {ReplyOptions} [reply] The options for replying to a message + * @property {MessageReferenceOptions} [messageReference] The options for a reference to a message */ /** @@ -145,7 +142,7 @@ class TextBasedChannel { * @example * // Send a remote file * channel.send({ - * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] + * files: ['https://github.com/discordjs.png'] * }) * .then(console.log) * .catch(console.error); @@ -161,27 +158,8 @@ class TextBasedChannel { * .then(console.log) * .catch(console.error); */ - async send(options) { - const { User } = require('../User.js'); - const { GuildMember } = require('../GuildMember.js'); - - 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); + send(options) { + return this.client.channels.createMessage(this, options); } /** @@ -386,24 +364,23 @@ 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', + 'send', + ]; + for (const prop of props) { if (ignore.includes(prop)) continue; Object.defineProperty( diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 2d685407a..1a37649fb 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2175,6 +2175,9 @@ export class Message extends Base { public reply( options: string | MessagePayload | MessageReplyOptions, ): Promise>>; + public forward( + channel: Exclude, + ): Promise>; public resolveComponent(customId: string): MessageActionRowComponent | null; public startThread(options: StartThreadOptions): Promise>; public suppressEmbeds(suppress?: boolean): Promise>>; @@ -4026,6 +4029,10 @@ export class CategoryChannelChildManager extends DataManager { private constructor(client: Client, iterable: Iterable); + public createMessage( + channel: Exclude, + options: string | MessagePayload | MessageCreateOptions, + ): Promise>; public fetch(id: Snowflake, options?: FetchChannelOptions): Promise; } @@ -6332,7 +6339,7 @@ export interface MessageCreateOptions extends BaseMessageOptionsWithPoll { tts?: boolean; nonce?: string | number; enforceNonce?: boolean; - reply?: ReplyOptions; + messageReference?: MessageReferenceOptions; stickers?: readonly StickerResolvable[]; flags?: | BitFieldResolvable< @@ -6365,6 +6372,10 @@ export interface MessageReference { type: MessageReferenceType; } +export interface MessageReferenceOptions extends MessageReference { + failIfNotExists?: boolean; +} + export type MessageResolvable = Message | Snowflake; export interface BaseSelectMenuComponentData extends BaseComponentData { @@ -6437,15 +6448,14 @@ export interface TextInputComponentData extends BaseComponentData { } export type MessageTarget = + | ChannelManager | Interaction | InteractionWebhook - | TextBasedChannel - | User - | GuildMember - | Webhook - | WebhookClient | Message - | MessageManager; + | MessageManager + | TextBasedChannel + | Webhook + | WebhookClient; export interface MultipleShardRespawnOptions { shardDelay?: number; @@ -6609,12 +6619,7 @@ export interface ReactionCollectorOptions extends CollectorOptions<[MessageReact maxUsers?: number; } -export interface ReplyOptions { - messageReference: MessageResolvable; - failIfNotExists?: boolean; -} - -export interface MessageReplyOptions extends Omit { +export interface MessageReplyOptions extends Omit { failIfNotExists?: boolean; } @@ -6783,26 +6788,26 @@ 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; +export type SendableChannels = Extract any }>; + export type CategoryChildChannel = Exclude, CategoryChannel>; export type NonThreadGuildBasedChannel = Exclude; export type GuildTextBasedChannel = Extract; +export type SendableChannelTypes = SendableChannels['type']; + export type TextChannelResolvable = Snowflake | TextChannel; export type TextBasedChannelResolvable = Snowflake | TextBasedChannel; @@ -6893,7 +6898,8 @@ export interface WebhookFetchMessageOptions { threadId?: Snowflake; } -export interface WebhookMessageCreateOptions extends Omit { +export interface WebhookMessageCreateOptions + extends Omit { username?: string; avatarURL?: string; threadId?: Snowflake; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 77a383150..7b9aea274 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -432,12 +432,20 @@ client.on('messageCreate', async message => { 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); const component = await message.awaitMessageComponent({ componentType: ComponentType.Button }); @@ -467,8 +475,13 @@ client.on('messageCreate', async message => { // @ts-expect-error channel.send(); // @ts-expect-error + client.channels.createMessage(); + // @ts-expect-error channel.send({ another: 'property' }); - + // @ts-expect-error + client.channels.createMessage({ another: 'property' }); + // @ts-expect-error + client.channels.createMessage('string'); // Check collector creations. // Verify that buttons interactions are inferred. @@ -647,7 +660,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], }); @@ -1334,7 +1347,7 @@ client.on('guildCreate', async g => { ], }); - channel.send({ components: [row, row2] }); + client.channels.createMessage(channel, { components: [row, row2] }); } channel.setName('foo').then(updatedChannel => { @@ -2704,7 +2717,7 @@ declare const sku: SKU; }); } -await textChannel.send({ +await client.channels.createMessage('123', { poll: { question: { text: 'Question',