mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-14 18:43:31 +01:00
feat!: create forwards and add ChannelManager#createMessage() (#10559)
BREAKING CHANGE: `MessageCreateOptions` no longer accepts `forward` or `reply`. Use `messageReference` instead. --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
@@ -1,13 +1,17 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const process = require('node:process');
|
const process = require('node:process');
|
||||||
|
const { lazy } = require('@discordjs/util');
|
||||||
const { Routes } = require('discord-api-types/v10');
|
const { Routes } = require('discord-api-types/v10');
|
||||||
const { CachedManager } = require('./CachedManager.js');
|
const { CachedManager } = require('./CachedManager.js');
|
||||||
const { BaseChannel } = require('../structures/BaseChannel.js');
|
const { BaseChannel } = require('../structures/BaseChannel.js');
|
||||||
|
const { MessagePayload } = require('../structures/MessagePayload.js');
|
||||||
const { createChannel } = require('../util/Channels.js');
|
const { createChannel } = require('../util/Channels.js');
|
||||||
const { ThreadChannelTypes } = require('../util/Constants.js');
|
const { ThreadChannelTypes } = require('../util/Constants.js');
|
||||||
const { Events } = require('../util/Events.js');
|
const { Events } = require('../util/Events.js');
|
||||||
|
|
||||||
|
const getMessage = lazy(() => require('../structures/Message.js').Message);
|
||||||
|
|
||||||
let cacheWarningEmitted = false;
|
let cacheWarningEmitted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,6 +127,52 @@ class ChannelManager extends CachedManager {
|
|||||||
const data = await this.client.rest.get(Routes.channel(id));
|
const data = await this.client.rest.get(Routes.channel(id));
|
||||||
return this._add(data, null, { cache, allowUnknownGuild });
|
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<Message>}
|
||||||
|
* @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;
|
exports.ChannelManager = ChannelManager;
|
||||||
|
|||||||
@@ -191,6 +191,6 @@ class BaseGuildTextChannel extends GuildChannel {
|
|||||||
setNSFW() {}
|
setNSFW() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(BaseGuildTextChannel, true);
|
TextBasedChannel.applyToClass(BaseGuildTextChannel);
|
||||||
|
|
||||||
exports.BaseGuildTextChannel = BaseGuildTextChannel;
|
exports.BaseGuildTextChannel = BaseGuildTextChannel;
|
||||||
|
|||||||
@@ -229,6 +229,6 @@ class BaseGuildVoiceChannel extends GuildChannel {
|
|||||||
setNSFW() {}
|
setNSFW() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(BaseGuildVoiceChannel, true, ['lastPinAt']);
|
TextBasedChannel.applyToClass(BaseGuildVoiceChannel, ['lastPinAt']);
|
||||||
|
|
||||||
exports.BaseGuildVoiceChannel = BaseGuildVoiceChannel;
|
exports.BaseGuildVoiceChannel = BaseGuildVoiceChannel;
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class DMChannel extends BaseChannel {
|
|||||||
// Doesn't work on DM channels; setNSFW() {}
|
// Doesn't work on DM channels; setNSFW() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(DMChannel, true, [
|
TextBasedChannel.applyToClass(DMChannel, [
|
||||||
'bulkDelete',
|
'bulkDelete',
|
||||||
'fetchWebhooks',
|
'fetchWebhooks',
|
||||||
'createWebhook',
|
'createWebhook',
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
const { PermissionFlagsBits } = require('discord-api-types/v10');
|
const { PermissionFlagsBits } = require('discord-api-types/v10');
|
||||||
const { Base } = require('./Base.js');
|
const { Base } = require('./Base.js');
|
||||||
const { VoiceState } = require('./VoiceState.js');
|
const { VoiceState } = require('./VoiceState.js');
|
||||||
const { TextBasedChannel } = require('./interfaces/TextBasedChannel.js');
|
|
||||||
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
|
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
|
||||||
const { GuildMemberRoleManager } = require('../managers/GuildMemberRoleManager.js');
|
const { GuildMemberRoleManager } = require('../managers/GuildMemberRoleManager.js');
|
||||||
const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField.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.
|
* Represents a member of a guild on Discord.
|
||||||
* @implements {TextBasedChannel}
|
|
||||||
* @extends {Base}
|
* @extends {Base}
|
||||||
*/
|
*/
|
||||||
class GuildMember 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 });
|
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<Message>}
|
||||||
|
* @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
|
* 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
|
* 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<Message>}
|
|
||||||
* @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;
|
exports.GuildMember = GuildMember;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const {
|
|||||||
ChannelType,
|
ChannelType,
|
||||||
MessageType,
|
MessageType,
|
||||||
MessageFlags,
|
MessageFlags,
|
||||||
|
MessageReferenceType,
|
||||||
PermissionFlagsBits,
|
PermissionFlagsBits,
|
||||||
} = require('discord-api-types/v10');
|
} = require('discord-api-types/v10');
|
||||||
const { Attachment } = require('./Attachment.js');
|
const { Attachment } = require('./Attachment.js');
|
||||||
@@ -680,7 +681,11 @@ class Message extends Base {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get editable() {
|
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
|
// 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.
|
// 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}"`))
|
* .then(() => console.log(`Replied to message "${message.content}"`))
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async reply(options) {
|
reply(options) {
|
||||||
if (!this.channel) throw new DiscordjsError(ErrorCodes.ChannelNotCached);
|
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
if (options instanceof MessagePayload) {
|
if (options instanceof MessagePayload) {
|
||||||
data = options;
|
data = options;
|
||||||
} else {
|
} else {
|
||||||
data = MessagePayload.create(this, options, {
|
data = MessagePayload.create(this, options, {
|
||||||
reply: {
|
messageReference: {
|
||||||
messageReference: this,
|
messageId: this.id,
|
||||||
|
channelId: this.channelId,
|
||||||
|
guildId: this.guildId ?? undefined,
|
||||||
|
type: MessageReferenceType.Default,
|
||||||
failIfNotExists: options?.failIfNotExists ?? this.client.options.failIfNotExists,
|
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<Message>}
|
||||||
|
*/
|
||||||
|
forward(channel) {
|
||||||
|
return this.client.channels.createMessage(channel, {
|
||||||
|
messageReference: {
|
||||||
|
messageId: this.id,
|
||||||
|
channelId: this.channelId,
|
||||||
|
guildId: this.guildId ?? undefined,
|
||||||
|
type: MessageReferenceType.Forward,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class MessagePayload {
|
|||||||
if (
|
if (
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
this.options.flags != null ||
|
this.options.flags != null ||
|
||||||
(this.isMessage && this.options.reply === undefined) ||
|
(this.isMessage && this.options.messageReference === undefined) ||
|
||||||
this.isMessageManager
|
this.isMessageManager
|
||||||
) {
|
) {
|
||||||
flags = new MessageFlagsBitField(this.options.flags).bitfield;
|
flags = new MessageFlagsBitField(this.options.flags).bitfield;
|
||||||
@@ -168,13 +168,16 @@ class MessagePayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let message_reference;
|
let message_reference;
|
||||||
if (typeof this.options.reply === 'object') {
|
if (this.options.messageReference) {
|
||||||
const reference = this.options.reply.messageReference;
|
const reference = this.options.messageReference;
|
||||||
const message_id = this.isMessage ? (reference.id ?? reference) : this.target.messages.resolveId(reference);
|
|
||||||
if (message_id) {
|
if (reference.messageId) {
|
||||||
message_reference = {
|
message_reference = {
|
||||||
message_id,
|
message_id: reference.messageId,
|
||||||
fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists,
|
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.
|
* A target for a message.
|
||||||
* @typedef {TextBasedChannels|User|GuildMember|Webhook|WebhookClient|BaseInteraction|InteractionWebhook|
|
* @typedef {TextBasedChannels|ChannelManager|Webhook|WebhookClient|BaseInteraction|InteractionWebhook|
|
||||||
* Message|MessageManager} MessageTarget
|
* Message|MessageManager} MessageTarget
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class PartialGroupDMChannel extends BaseChannel {
|
|||||||
awaitMessageComponent() {}
|
awaitMessageComponent() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(PartialGroupDMChannel, true, [
|
TextBasedChannel.applyToClass(PartialGroupDMChannel, [
|
||||||
'bulkDelete',
|
'bulkDelete',
|
||||||
'send',
|
'send',
|
||||||
'sendTyping',
|
'sendTyping',
|
||||||
|
|||||||
@@ -598,6 +598,6 @@ class ThreadChannel extends BaseChannel {
|
|||||||
// Doesn't work on Thread channels; setNSFW() {}
|
// Doesn't work on Thread channels; setNSFW() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(ThreadChannel, true, ['fetchWebhooks', 'setRateLimitPerUser', 'setNSFW']);
|
TextBasedChannel.applyToClass(ThreadChannel, ['fetchWebhooks', 'setRateLimitPerUser', 'setNSFW']);
|
||||||
|
|
||||||
exports.ThreadChannel = ThreadChannel;
|
exports.ThreadChannel = ThreadChannel;
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ class ThreadOnlyChannel extends GuildChannel {
|
|||||||
setRateLimitPerUser() {}
|
setRateLimitPerUser() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(ThreadOnlyChannel, true, [
|
TextBasedChannel.applyToClass(ThreadOnlyChannel, [
|
||||||
'send',
|
'send',
|
||||||
'lastMessage',
|
'lastMessage',
|
||||||
'lastPinAt',
|
'lastPinAt',
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ const { userMention } = require('@discordjs/formatters');
|
|||||||
const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest');
|
const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest');
|
||||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||||
const { Base } = require('./Base.js');
|
const { Base } = require('./Base.js');
|
||||||
const { TextBasedChannel } = require('./interfaces/TextBasedChannel.js');
|
|
||||||
const { UserFlagsBitField } = require('../util/UserFlagsBitField.js');
|
const { UserFlagsBitField } = require('../util/UserFlagsBitField.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a user on Discord.
|
* Represents a user on Discord.
|
||||||
* @implements {TextBasedChannel}
|
|
||||||
* @extends {Base}
|
* @extends {Base}
|
||||||
*/
|
*/
|
||||||
class User extends Base {
|
class User extends Base {
|
||||||
@@ -277,6 +275,22 @@ class User extends Base {
|
|||||||
return this.client.users.deleteDM(this.id);
|
return this.client.users.deleteDM(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to this user.
|
||||||
|
* @param {string|MessagePayload|MessageCreateOptions} options The options to provide
|
||||||
|
* @returns {Promise<Message>}
|
||||||
|
* @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.
|
* Checks if the user is equal to another.
|
||||||
* It compares id, username, discriminator, avatar, banner, accent color, and bot flags.
|
* 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<Message>}
|
|
||||||
* @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;
|
exports.User = User;
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ class Webhook {
|
|||||||
* @example
|
* @example
|
||||||
* // Send a remote file
|
* // Send a remote file
|
||||||
* webhook.send({
|
* webhook.send({
|
||||||
* files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
|
* files: ['https://github.com/discordjs.png']
|
||||||
* })
|
* })
|
||||||
* .then(console.log)
|
* .then(console.log)
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors
|
|||||||
const { MaxBulkDeletableMessageAge } = require('../../util/Constants.js');
|
const { MaxBulkDeletableMessageAge } = require('../../util/Constants.js');
|
||||||
const { InteractionCollector } = require('../InteractionCollector.js');
|
const { InteractionCollector } = require('../InteractionCollector.js');
|
||||||
const { MessageCollector } = require('../MessageCollector.js');
|
const { MessageCollector } = require('../MessageCollector.js');
|
||||||
const { MessagePayload } = require('../MessagePayload.js');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for classes that have text-channel-like features.
|
* 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
|
* @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.
|
* The options for sending a message.
|
||||||
* @typedef {BaseMessageOptionsWithPoll} BaseMessageCreateOptions
|
* @typedef {BaseMessageOptionsWithPoll} BaseMessageCreateOptions
|
||||||
@@ -110,10 +101,16 @@ class TextBasedChannel {
|
|||||||
* <info>Only `MessageFlags.SuppressEmbeds` and `MessageFlags.SuppressNotifications` can be set.</info>
|
* <info>Only `MessageFlags.SuppressEmbeds` and `MessageFlags.SuppressNotifications` can be set.</info>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
* The options for sending a message.
|
||||||
* @typedef {BaseMessageCreateOptions} MessageCreateOptions
|
* @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
|
* @example
|
||||||
* // Send a remote file
|
* // Send a remote file
|
||||||
* channel.send({
|
* channel.send({
|
||||||
* files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
|
* files: ['https://github.com/discordjs.png']
|
||||||
* })
|
* })
|
||||||
* .then(console.log)
|
* .then(console.log)
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
@@ -161,27 +158,8 @@ class TextBasedChannel {
|
|||||||
* .then(console.log)
|
* .then(console.log)
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async send(options) {
|
send(options) {
|
||||||
const { User } = require('../User.js');
|
return this.client.channels.createMessage(this, options);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -386,24 +364,23 @@ class TextBasedChannel {
|
|||||||
return this.edit({ nsfw, reason });
|
return this.edit({ nsfw, reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyToClass(structure, full = false, ignore = []) {
|
static applyToClass(structure, ignore = []) {
|
||||||
const props = ['send'];
|
const props = [
|
||||||
if (full) {
|
'lastMessage',
|
||||||
props.push(
|
'lastPinAt',
|
||||||
'lastMessage',
|
'bulkDelete',
|
||||||
'lastPinAt',
|
'sendTyping',
|
||||||
'bulkDelete',
|
'createMessageCollector',
|
||||||
'sendTyping',
|
'awaitMessages',
|
||||||
'createMessageCollector',
|
'createMessageComponentCollector',
|
||||||
'awaitMessages',
|
'awaitMessageComponent',
|
||||||
'createMessageComponentCollector',
|
'fetchWebhooks',
|
||||||
'awaitMessageComponent',
|
'createWebhook',
|
||||||
'fetchWebhooks',
|
'setRateLimitPerUser',
|
||||||
'createWebhook',
|
'setNSFW',
|
||||||
'setRateLimitPerUser',
|
'send',
|
||||||
'setNSFW',
|
];
|
||||||
);
|
|
||||||
}
|
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
if (ignore.includes(prop)) continue;
|
if (ignore.includes(prop)) continue;
|
||||||
Object.defineProperty(
|
Object.defineProperty(
|
||||||
|
|||||||
42
packages/discord.js/typings/index.d.ts
vendored
42
packages/discord.js/typings/index.d.ts
vendored
@@ -2175,6 +2175,9 @@ export class Message<InGuild extends boolean = boolean> extends Base {
|
|||||||
public reply(
|
public reply(
|
||||||
options: string | MessagePayload | MessageReplyOptions,
|
options: string | MessagePayload | MessageReplyOptions,
|
||||||
): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
|
): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
|
||||||
|
public forward(
|
||||||
|
channel: Exclude<TextBasedChannelResolvable, PartialGroupDMChannel>,
|
||||||
|
): Promise<OmitPartialGroupDMChannel<Message>>;
|
||||||
public resolveComponent(customId: string): MessageActionRowComponent | null;
|
public resolveComponent(customId: string): MessageActionRowComponent | null;
|
||||||
public startThread(options: StartThreadOptions): Promise<PublicThreadChannel<false>>;
|
public startThread(options: StartThreadOptions): Promise<PublicThreadChannel<false>>;
|
||||||
public suppressEmbeds(suppress?: boolean): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
|
public suppressEmbeds(suppress?: boolean): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
|
||||||
@@ -4026,6 +4029,10 @@ export class CategoryChannelChildManager extends DataManager<Snowflake, Category
|
|||||||
|
|
||||||
export class ChannelManager extends CachedManager<Snowflake, Channel, ChannelResolvable> {
|
export class ChannelManager extends CachedManager<Snowflake, Channel, ChannelResolvable> {
|
||||||
private constructor(client: Client<true>, iterable: Iterable<RawChannelData>);
|
private constructor(client: Client<true>, iterable: Iterable<RawChannelData>);
|
||||||
|
public createMessage(
|
||||||
|
channel: Exclude<TextBasedChannelResolvable, PartialGroupDMChannel>,
|
||||||
|
options: string | MessagePayload | MessageCreateOptions,
|
||||||
|
): Promise<OmitPartialGroupDMChannel<Message>>;
|
||||||
public fetch(id: Snowflake, options?: FetchChannelOptions): Promise<Channel | null>;
|
public fetch(id: Snowflake, options?: FetchChannelOptions): Promise<Channel | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6332,7 +6339,7 @@ export interface MessageCreateOptions extends BaseMessageOptionsWithPoll {
|
|||||||
tts?: boolean;
|
tts?: boolean;
|
||||||
nonce?: string | number;
|
nonce?: string | number;
|
||||||
enforceNonce?: boolean;
|
enforceNonce?: boolean;
|
||||||
reply?: ReplyOptions;
|
messageReference?: MessageReferenceOptions;
|
||||||
stickers?: readonly StickerResolvable[];
|
stickers?: readonly StickerResolvable[];
|
||||||
flags?:
|
flags?:
|
||||||
| BitFieldResolvable<
|
| BitFieldResolvable<
|
||||||
@@ -6365,6 +6372,10 @@ export interface MessageReference {
|
|||||||
type: MessageReferenceType;
|
type: MessageReferenceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MessageReferenceOptions extends MessageReference {
|
||||||
|
failIfNotExists?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageResolvable = Message | Snowflake;
|
export type MessageResolvable = Message | Snowflake;
|
||||||
|
|
||||||
export interface BaseSelectMenuComponentData extends BaseComponentData {
|
export interface BaseSelectMenuComponentData extends BaseComponentData {
|
||||||
@@ -6437,15 +6448,14 @@ export interface TextInputComponentData extends BaseComponentData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type MessageTarget =
|
export type MessageTarget =
|
||||||
|
| ChannelManager
|
||||||
| Interaction
|
| Interaction
|
||||||
| InteractionWebhook
|
| InteractionWebhook
|
||||||
| TextBasedChannel
|
|
||||||
| User
|
|
||||||
| GuildMember
|
|
||||||
| Webhook<WebhookType.Incoming>
|
|
||||||
| WebhookClient
|
|
||||||
| Message
|
| Message
|
||||||
| MessageManager;
|
| MessageManager
|
||||||
|
| TextBasedChannel
|
||||||
|
| Webhook<WebhookType.Incoming>
|
||||||
|
| WebhookClient;
|
||||||
|
|
||||||
export interface MultipleShardRespawnOptions {
|
export interface MultipleShardRespawnOptions {
|
||||||
shardDelay?: number;
|
shardDelay?: number;
|
||||||
@@ -6609,12 +6619,7 @@ export interface ReactionCollectorOptions extends CollectorOptions<[MessageReact
|
|||||||
maxUsers?: number;
|
maxUsers?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReplyOptions {
|
export interface MessageReplyOptions extends Omit<MessageCreateOptions, 'messageReference'> {
|
||||||
messageReference: MessageResolvable;
|
|
||||||
failIfNotExists?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MessageReplyOptions extends Omit<MessageCreateOptions, 'reply'> {
|
|
||||||
failIfNotExists?: boolean;
|
failIfNotExists?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6783,26 +6788,26 @@ export type Channel =
|
|||||||
|
|
||||||
export type TextBasedChannel = Exclude<Extract<Channel, { type: TextChannelType }>, ForumChannel | MediaChannel>;
|
export type TextBasedChannel = Exclude<Extract<Channel, { type: TextChannelType }>, ForumChannel | MediaChannel>;
|
||||||
|
|
||||||
export type SendableChannels = Extract<Channel, { send: (...args: any[]) => any }>;
|
|
||||||
|
|
||||||
export type TextBasedChannels = TextBasedChannel;
|
export type TextBasedChannels = TextBasedChannel;
|
||||||
|
|
||||||
export type TextBasedChannelTypes = TextBasedChannel['type'];
|
export type TextBasedChannelTypes = TextBasedChannel['type'];
|
||||||
|
|
||||||
export type GuildTextBasedChannelTypes = Exclude<TextBasedChannelTypes, ChannelType.DM | ChannelType.GroupDM>;
|
export type GuildTextBasedChannelTypes = Exclude<TextBasedChannelTypes, ChannelType.DM | ChannelType.GroupDM>;
|
||||||
|
|
||||||
export type SendableChannelTypes = SendableChannels['type'];
|
|
||||||
|
|
||||||
export type VoiceBasedChannel = Extract<Channel, { bitrate: number }>;
|
export type VoiceBasedChannel = Extract<Channel, { bitrate: number }>;
|
||||||
|
|
||||||
export type GuildBasedChannel = Extract<Channel, { guild: Guild }>;
|
export type GuildBasedChannel = Extract<Channel, { guild: Guild }>;
|
||||||
|
|
||||||
|
export type SendableChannels = Extract<Channel, { send: (...args: any[]) => any }>;
|
||||||
|
|
||||||
export type CategoryChildChannel = Exclude<Extract<Channel, { parent: CategoryChannel | null }>, CategoryChannel>;
|
export type CategoryChildChannel = Exclude<Extract<Channel, { parent: CategoryChannel | null }>, CategoryChannel>;
|
||||||
|
|
||||||
export type NonThreadGuildBasedChannel = Exclude<GuildBasedChannel, AnyThreadChannel>;
|
export type NonThreadGuildBasedChannel = Exclude<GuildBasedChannel, AnyThreadChannel>;
|
||||||
|
|
||||||
export type GuildTextBasedChannel = Extract<GuildBasedChannel, TextBasedChannel>;
|
export type GuildTextBasedChannel = Extract<GuildBasedChannel, TextBasedChannel>;
|
||||||
|
|
||||||
|
export type SendableChannelTypes = SendableChannels['type'];
|
||||||
|
|
||||||
export type TextChannelResolvable = Snowflake | TextChannel;
|
export type TextChannelResolvable = Snowflake | TextChannel;
|
||||||
|
|
||||||
export type TextBasedChannelResolvable = Snowflake | TextBasedChannel;
|
export type TextBasedChannelResolvable = Snowflake | TextBasedChannel;
|
||||||
@@ -6893,7 +6898,8 @@ export interface WebhookFetchMessageOptions {
|
|||||||
threadId?: Snowflake;
|
threadId?: Snowflake;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebhookMessageCreateOptions extends Omit<MessageCreateOptions, 'nonce' | 'reply' | 'stickers'> {
|
export interface WebhookMessageCreateOptions
|
||||||
|
extends Omit<MessageCreateOptions, 'nonce' | 'messageReference' | 'stickers'> {
|
||||||
username?: string;
|
username?: string;
|
||||||
avatarURL?: string;
|
avatarURL?: string;
|
||||||
threadId?: Snowflake;
|
threadId?: Snowflake;
|
||||||
|
|||||||
@@ -432,12 +432,20 @@ client.on('messageCreate', async message => {
|
|||||||
assertIsMessage(channel.send({}));
|
assertIsMessage(channel.send({}));
|
||||||
assertIsMessage(channel.send({ embeds: [] }));
|
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 attachment = new AttachmentBuilder('file.png');
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
assertIsMessage(channel.send({ files: [attachment] }));
|
assertIsMessage(channel.send({ files: [attachment] }));
|
||||||
assertIsMessage(channel.send({ embeds: [embed] }));
|
assertIsMessage(channel.send({ embeds: [embed] }));
|
||||||
assertIsMessage(channel.send({ embeds: [embed], files: [attachment] }));
|
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()) {
|
if (message.inGuild()) {
|
||||||
expectAssignable<Message<true>>(message);
|
expectAssignable<Message<true>>(message);
|
||||||
const component = await message.awaitMessageComponent({ componentType: ComponentType.Button });
|
const component = await message.awaitMessageComponent({ componentType: ComponentType.Button });
|
||||||
@@ -467,8 +475,13 @@ client.on('messageCreate', async message => {
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
channel.send();
|
channel.send();
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
client.channels.createMessage();
|
||||||
|
// @ts-expect-error
|
||||||
channel.send({ another: 'property' });
|
channel.send({ another: 'property' });
|
||||||
|
// @ts-expect-error
|
||||||
|
client.channels.createMessage({ another: 'property' });
|
||||||
|
// @ts-expect-error
|
||||||
|
client.channels.createMessage('string');
|
||||||
// Check collector creations.
|
// Check collector creations.
|
||||||
|
|
||||||
// Verify that buttons interactions are inferred.
|
// Verify that buttons interactions are inferred.
|
||||||
@@ -647,7 +660,7 @@ client.on('messageCreate', async message => {
|
|||||||
|
|
||||||
const embedData = { description: 'test', color: 0xff0000 };
|
const embedData = { description: 'test', color: 0xff0000 };
|
||||||
|
|
||||||
channel.send({
|
client.channels.createMessage(channel, {
|
||||||
components: [row, rawButtonsRow, buttonsRow, rawStringSelectMenuRow, stringSelectRow],
|
components: [row, rawButtonsRow, buttonsRow, rawStringSelectMenuRow, stringSelectRow],
|
||||||
embeds: [embed, embedData],
|
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 => {
|
channel.setName('foo').then(updatedChannel => {
|
||||||
@@ -2704,7 +2717,7 @@ declare const sku: SKU;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await textChannel.send({
|
await client.channels.createMessage('123', {
|
||||||
poll: {
|
poll: {
|
||||||
question: {
|
question: {
|
||||||
text: 'Question',
|
text: 'Question',
|
||||||
|
|||||||
Reference in New Issue
Block a user