feat: message forwards

#10733 djs
This commit is contained in:
Elysia
2025-02-14 22:41:15 +07:00
parent 4dabf78067
commit 413bf9d981
4 changed files with 54 additions and 23 deletions

View File

@@ -603,7 +603,10 @@ class Message extends Base {
*/ */
get editable() { get editable() {
const precheck = Boolean( const precheck = Boolean(
this.author.id === this.client.user.id && !deletedMessages.has(this) && (!this.guild || this.channel?.viewable), this.author.id === this.client.user.id &&
!deletedMessages.has(this) &&
(!this.guild || this.channel?.viewable) &&
this.reference?.type !== 'FORWARD',
); );
// Regardless of permissions thread messages cannot be edited if // Regardless of permissions thread messages cannot be edited if
@@ -871,26 +874,20 @@ class Message extends Base {
} }
/** /**
* Forwards this message to a channel. * Forwards this message
* @param {TextBasedChannelResolvable} channel The channel to forward the message to * @param {TextBasedChannelResolvable} channel The channel to forward this message to.
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
forward(channel) { forward(channel) {
channel = this.client.channels.resolve(channel); const resolvedChannel = this.client.channels.resolve(channel);
if (!channel || !this.channelId) return Promise.reject(new Error('CHANNEL_NOT_CACHED')); if (!resolvedChannel) throw new Error('INVALID_TYPE', 'channel', 'TextBasedChannelResolvable');
const data = MessagePayload.create( return resolvedChannel.send({
this, forward: {
{}, message: this.id,
{ channel: this.channelId,
forward: { guild: this.guildId,
channel_id: this.channelId,
guild_id: this.guildId,
message_id: this.id,
type: 1,
},
}, },
); });
return channel.send(data);
} }
/** /**

View File

@@ -3,7 +3,7 @@
const { Buffer } = require('node:buffer'); const { Buffer } = require('node:buffer');
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const MessageEmbed = require('./MessageEmbed'); const MessageEmbed = require('./MessageEmbed');
const { RangeError } = require('../errors'); const { RangeError, Error: DjsError } = require('../errors');
const ActivityFlags = require('../util/ActivityFlags'); const ActivityFlags = require('../util/ActivityFlags');
const { PollLayoutTypes, MessageReferenceTypes } = require('../util/Constants'); const { PollLayoutTypes, MessageReferenceTypes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
@@ -190,9 +190,21 @@ class MessagePayload {
}; };
} }
} }
if (typeof this.options.forward === 'object') { if (typeof this.options.forward === 'object') {
message_reference = this.options.forward; const reference = this.options.forward.message;
message_reference.type = MessageReferenceTypes.FORWARD; const channel_id = reference.channelId ?? this.target.client.channels.resolveId(this.options.forward.channel);
const guild_id = reference.guildId ?? this.target.client.guilds.resolveId(this.options.forward.guild);
const message_id = this.target.messages.resolveId(reference);
if (message_id) {
if (!channel_id) throw new DjsError('INVALID_TYPE', 'channelId', 'TextBasedChannelResolvable');
message_reference = {
type: MessageReferenceTypes.FORWARD,
message_id,
channel_id,
guild_id: guild_id ?? undefined,
};
}
} }
const attachments = this.options.files?.map((file, index) => ({ const attachments = this.options.files?.map((file, index) => ({

View File

@@ -100,10 +100,18 @@ class TextBasedChannel {
* @property {PollData} [poll] The poll to send with the message * @property {PollData} [poll] The poll to send with the message
*/ */
/**
* @typedef {Object} ForwardOptions
* @property {MessageResolvable} message The originating message
* @property {TextBasedChannelResolvable} [channel] The channel of the originating message
* @property {GuildResolvable} [guild] The guild of the originating message
*/
/** /**
* Options provided when sending or editing a message. * Options provided when sending or editing a message.
* @typedef {BaseMessageOptions} MessageOptions * @typedef {BaseMessageOptions} MessageOptions
* @property {ReplyOptions} [reply] The options for replying to a message * @property {ReplyOptions} [reply] The options for replying to a message
* @property {ForwardOptions} [forward] The options for forwarding a message
* @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message * @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message
* @property {MessageFlags} [flags] Which flags to set for the message. * @property {MessageFlags} [flags] Which flags to set for the message.
* Only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_VOICE_MESSAGE` can be set. * Only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_VOICE_MESSAGE` can be set.

20
typings/index.d.ts vendored
View File

@@ -7186,6 +7186,7 @@ export interface MessageOptions {
allowedMentions?: MessageMentionOptions; allowedMentions?: MessageMentionOptions;
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
reply?: ReplyOptions; reply?: ReplyOptions;
forward?: ForwardOptions;
stickers?: StickerResolvable[]; stickers?: StickerResolvable[];
attachments?: MessageAttachment[]; attachments?: MessageAttachment[];
flags?: BitFieldResolvable<'SUPPRESS_EMBEDS' | 'SUPPRESS_NOTIFICATIONS' | 'IS_VOICE_MESSAGE', number>; flags?: BitFieldResolvable<'SUPPRESS_EMBEDS' | 'SUPPRESS_NOTIFICATIONS' | 'IS_VOICE_MESSAGE', number>;
@@ -7496,10 +7497,23 @@ export interface ReplyOptions {
failIfNotExists?: boolean; failIfNotExists?: boolean;
} }
export interface ReplyMessageOptions extends Omit<MessageOptions, 'reply'> { export interface BaseForwardOptions {
failIfNotExists?: boolean; message: MessageResolvable;
channel?: TextBasedChannelResolvable;
guild?: GuildResolvable;
} }
export type ForwardOptionsWithMandatoryChannel = BaseForwardOptions & Required<Pick<BaseForwardOptions, 'channel'>>;
export interface ForwardOptionsWithOptionalChannel extends BaseForwardOptions {
message: Exclude<MessageResolvable, Snowflake>;
}
export type ForwardOptions = ForwardOptionsWithMandatoryChannel | ForwardOptionsWithOptionalChannel;
export interface ReplyMessageOptions extends Omit<MessageOptions, 'reply' | 'forward'> {
failIfNotExists?: boolean;
}
export interface ResolvedOverwriteOptions { export interface ResolvedOverwriteOptions {
allow: Permissions; allow: Permissions;
deny: Permissions; deny: Permissions;
@@ -7832,7 +7846,7 @@ export interface WebhookFetchMessageOptions {
threadId?: Snowflake; threadId?: Snowflake;
} }
export interface WebhookMessageOptions extends Omit<MessageOptions, 'reply' | 'stickers'> { export interface WebhookMessageOptions extends Omit<MessageOptions, 'nonce' | 'reply' | 'stickers' | 'forward'> {
username?: string; username?: string;
avatarURL?: string; avatarURL?: string;
threadId?: Snowflake; threadId?: Snowflake;